TimeFeatures 1.0.11__tar.gz → 1.0.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/PKG-INFO +1 -1
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/TimeFeatures.egg-info/PKG-INFO +1 -1
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/setup.py +1 -1
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/owsavetodb.py +107 -18
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/owtimefeaturesconstructor.py +45 -1
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/owvardependencygraph.py +5 -1
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/LICENSE +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/README.md +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/TimeFeatures.egg-info/SOURCES.txt +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/TimeFeatures.egg-info/dependency_links.txt +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/TimeFeatures.egg-info/entry_points.txt +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/TimeFeatures.egg-info/top_level.txt +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/setup.cfg +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/__init__.py +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/icons/graphgenerator.svg +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/icons/savedatadb.svg +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/icons/timefeature-xs.svg +0 -0
- {TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/icons/timefeature.svg +0 -0
|
@@ -21,7 +21,7 @@ setup(name="TimeFeatures",
|
|
|
21
21
|
packages=["timefeatures.widgets"],
|
|
22
22
|
package_data={"timefeatures.widgets": ["icons/*.svg", "icons/*.png"]},
|
|
23
23
|
entry_points={"orange.widgets": "Time-Features = timefeatures.widgets"},
|
|
24
|
-
version="1.0.
|
|
24
|
+
version="1.0.13",
|
|
25
25
|
author="Alejandro Rivas García",
|
|
26
26
|
author_email="alejandrorivasgarcia@gmail.com",
|
|
27
27
|
keywords=[
|
|
@@ -19,6 +19,12 @@ from PyQt5.QtGui import QPixmap, QStandardItem
|
|
|
19
19
|
from PyQt5.QtWidgets import QGridLayout, QLineEdit, QPushButton, QSizePolicy, QLabel
|
|
20
20
|
from orangewidget.utils.signals import Input
|
|
21
21
|
|
|
22
|
+
import re
|
|
23
|
+
|
|
24
|
+
import smtplib, ssl, time
|
|
25
|
+
from email.mime.text import MIMEText
|
|
26
|
+
from email.mime.multipart import MIMEMultipart
|
|
27
|
+
|
|
22
28
|
MAX_DL_LIMIT = 1000000
|
|
23
29
|
|
|
24
30
|
|
|
@@ -131,6 +137,9 @@ class owsavetodb(OWBaseSql, OWWidget):
|
|
|
131
137
|
self.cols_label = QLabel()
|
|
132
138
|
self.cols_label.setText("Columns: 0")
|
|
133
139
|
layoutA.addWidget(self.cols_label, 2, 0)
|
|
140
|
+
self.emailDirection = QLineEdit(
|
|
141
|
+
placeholderText="Email... (Optional)", toolTip="Email direction")
|
|
142
|
+
layoutA.addWidget(self.emailDirection, 4, 0)
|
|
134
143
|
self.tableName = QLineEdit(
|
|
135
144
|
placeholderText="Table name...", toolTip="Table name")
|
|
136
145
|
layoutA.addWidget(self.tableName, 3, 0)
|
|
@@ -140,7 +149,7 @@ class owsavetodb(OWBaseSql, OWWidget):
|
|
|
140
149
|
)
|
|
141
150
|
self.btn_savedata.clicked.connect(self.saveData)
|
|
142
151
|
self.btn_savedata.setEnabled(False)
|
|
143
|
-
layoutA.addWidget(self.btn_savedata,
|
|
152
|
+
layoutA.addWidget(self.btn_savedata, 4, 2)
|
|
144
153
|
self._add_backend_controls()
|
|
145
154
|
|
|
146
155
|
def _add_backend_controls(self):
|
|
@@ -180,7 +189,7 @@ class owsavetodb(OWBaseSql, OWWidget):
|
|
|
180
189
|
self.Error.connection(str(ex))
|
|
181
190
|
|
|
182
191
|
def create_table(self, table_name):
|
|
183
|
-
|
|
192
|
+
start_time = time.time()
|
|
184
193
|
self.progressBarInit()
|
|
185
194
|
contBar = 0
|
|
186
195
|
contMetasOriginales = 0
|
|
@@ -246,35 +255,115 @@ class owsavetodb(OWBaseSql, OWWidget):
|
|
|
246
255
|
pass
|
|
247
256
|
except BackendError as ex:
|
|
248
257
|
self.Error.connection(str(ex))
|
|
249
|
-
|
|
258
|
+
|
|
259
|
+
self.progressBarFinished()
|
|
260
|
+
if str(self.emailDirection.text()) != "":
|
|
261
|
+
end_time = time.time()
|
|
262
|
+
time_elapsed = end_time - start_time
|
|
263
|
+
time_elapsed = round(time_elapsed, 3)
|
|
264
|
+
self.send_mail(str(self.emailDirection.text()), time_elapsed)
|
|
265
|
+
|
|
266
|
+
def send_mail(self, mail, time_elapsed):
|
|
267
|
+
|
|
268
|
+
# Configuración de la connexión
|
|
269
|
+
sender = 'savetodbodm@gmail.com'
|
|
270
|
+
password = 'arnj lakd lyol rakg'
|
|
271
|
+
server = 'smtp.gmail.com'
|
|
272
|
+
port = 587
|
|
273
|
+
|
|
274
|
+
# Configuración del destinatario
|
|
275
|
+
to = mail
|
|
276
|
+
|
|
277
|
+
# configuración de las cabeceras y del mensaje
|
|
278
|
+
message = MIMEMultipart("alternative")
|
|
279
|
+
|
|
280
|
+
current_datetime = datetime.now()
|
|
281
|
+
formatted_datetime = current_datetime.strftime('%d-%m-%Y %H:%M:%S')
|
|
282
|
+
|
|
283
|
+
message["Subject"] = "Upload completed - Save To DB - " + str(formatted_datetime)
|
|
284
|
+
message["From"] = sender
|
|
285
|
+
message["To"] = to
|
|
286
|
+
|
|
287
|
+
if self.data.domain.class_var:
|
|
288
|
+
class_name = self.data.domain.class_var.name
|
|
289
|
+
else:
|
|
290
|
+
class_name = None
|
|
291
|
+
|
|
292
|
+
body = f"""\
|
|
293
|
+
<html>
|
|
294
|
+
<head>
|
|
295
|
+
</head>
|
|
296
|
+
<body>
|
|
297
|
+
<h1>Save to DB - Widget</h1>
|
|
298
|
+
<p>Your data upload has been completed!</p>
|
|
299
|
+
<p>-Table information:</p>
|
|
300
|
+
<ul>
|
|
301
|
+
<li>Table name: {str(self.tableName.text())}.</li>
|
|
302
|
+
<li>{str(self.rows_label.text())}.</li>
|
|
303
|
+
<li>{str(self.cols_label.text())}.</li>
|
|
304
|
+
<li>Class name: {str(class_name)}</li>
|
|
305
|
+
<li>{str(self.target_label.text())}.</li>
|
|
306
|
+
</ul>
|
|
307
|
+
<p>-Connection information:</p>
|
|
308
|
+
<ul>
|
|
309
|
+
<li>Server name: {str(self.servertext.text())}</li>
|
|
310
|
+
<li>Database name: {str(self.databasetext.text())}</li>
|
|
311
|
+
<li>Time Elapsed: {str(time_elapsed)}s.</li>
|
|
312
|
+
</ul>
|
|
313
|
+
</body>
|
|
314
|
+
</html>
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
part = MIMEText(body, "html")
|
|
318
|
+
message.attach(part)
|
|
319
|
+
|
|
320
|
+
# Envío del mensaje
|
|
321
|
+
try:
|
|
322
|
+
context = ssl.create_default_context()
|
|
323
|
+
with smtplib.SMTP(server, port=port) as smtp:
|
|
324
|
+
smtp.starttls(context=context)
|
|
325
|
+
smtp.login(sender, password)
|
|
326
|
+
smtp.send_message(message)
|
|
327
|
+
except Exception as ex:
|
|
328
|
+
self.Error.connection(str(ex))
|
|
250
329
|
|
|
251
330
|
def saveData(self):
|
|
252
331
|
|
|
253
332
|
self.clear()
|
|
254
333
|
|
|
334
|
+
email_regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
|
|
335
|
+
|
|
255
336
|
if self.tableName.text() == "":
|
|
256
337
|
self.Error.connection("Table name must be filled.")
|
|
257
338
|
elif self.servertext.text() == "" or self.databasetext.text() == "":
|
|
258
339
|
self.Error.connection("Host and database fields must be filled.")
|
|
340
|
+
elif self.emailDirection.text() != "":
|
|
341
|
+
if not re.match(email_regex, self.emailDirection.text()):
|
|
342
|
+
self.Error.connection("The field email must be an email.")
|
|
343
|
+
else:
|
|
344
|
+
self.insert_data()
|
|
259
345
|
else:
|
|
260
|
-
self.
|
|
346
|
+
self.insert_data()
|
|
261
347
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
else:
|
|
265
|
-
class_name = None
|
|
348
|
+
def insert_data(self):
|
|
349
|
+
self.create_master_table()
|
|
266
350
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
351
|
+
if self.data.domain.class_var:
|
|
352
|
+
class_name = self.data.domain.class_var.name
|
|
353
|
+
else:
|
|
354
|
+
class_name = None
|
|
355
|
+
|
|
356
|
+
query = "INSERT INTO public.datasets (name, datetime, rows, cols, class, class_name) VALUES ('" + self.tableName.text().lower() + "','" + datetime.now().strftime(
|
|
357
|
+
'%Y-%m-%d %H:%M:%S') + "','" + str(self.rows) + "','" + str(self.cols) + "','" + str(
|
|
358
|
+
self.target) + "','" + str(class_name) + "');"
|
|
359
|
+
|
|
360
|
+
try:
|
|
361
|
+
with self.backend.execute_sql_query(query):
|
|
362
|
+
pass
|
|
363
|
+
self.create_table(self.tableName.text().lower())
|
|
364
|
+
except BackendError as ex:
|
|
365
|
+
self.Error.connection(str(ex))
|
|
270
366
|
|
|
271
|
-
try:
|
|
272
|
-
with self.backend.execute_sql_query(query):
|
|
273
|
-
pass
|
|
274
|
-
self.create_table(self.tableName.text().lower())
|
|
275
|
-
except BackendError as ex:
|
|
276
|
-
self.Error.connection(str(ex))
|
|
277
|
-
|
|
278
367
|
def highlight_error(self, text=""):
|
|
279
368
|
err = ['', 'QLineEdit {border: 2px solid red;}']
|
|
280
369
|
self.servertext.setStyleSheet(err['server' in text or 'host' in text])
|
{TimeFeatures-1.0.11 → TimeFeatures-1.0.13}/timefeatures/widgets/owtimefeaturesconstructor.py
RENAMED
|
@@ -819,6 +819,7 @@ class owtimefeaturesconstructor(OWWidget, ConcurrentWidgetMixin):
|
|
|
819
819
|
|
|
820
820
|
class Inputs:
|
|
821
821
|
data = Input("Data", Orange.data.Table)
|
|
822
|
+
expressions = Input("Variable Definitions", Orange.data.Table)
|
|
822
823
|
|
|
823
824
|
class Outputs:
|
|
824
825
|
data = Output("Data", Orange.data.Table)
|
|
@@ -1103,6 +1104,35 @@ class owtimefeaturesconstructor(OWWidget, ConcurrentWidgetMixin):
|
|
|
1103
1104
|
self.fix_button.setHidden(not self.expressions_with_values)
|
|
1104
1105
|
self.editorstack.setEnabled(self.currentIndex >= 0)
|
|
1105
1106
|
|
|
1107
|
+
@Inputs.expressions
|
|
1108
|
+
@check_sql_input
|
|
1109
|
+
def setExpressions(self, expressions=None):
|
|
1110
|
+
|
|
1111
|
+
self.expressions = expressions
|
|
1112
|
+
|
|
1113
|
+
if self.expressions is None:
|
|
1114
|
+
self.Warning.clear()
|
|
1115
|
+
self.Error.transform_error.clear()
|
|
1116
|
+
|
|
1117
|
+
if self.data is not None and self.expressions is not None:
|
|
1118
|
+
if self.expressions is not None:
|
|
1119
|
+
if len(self.expressions.domain) >= 2 and (
|
|
1120
|
+
self.expressions.domain[0].name != "Variable" or self.expressions.domain[1].name != "Expression"):
|
|
1121
|
+
self.Warning.table_warning()
|
|
1122
|
+
else:
|
|
1123
|
+
self.Error.transform_error.clear()
|
|
1124
|
+
for datos in reversed(self.expressions):
|
|
1125
|
+
if not math.isnan(datos[1]) and str(datos[1]) != "NaN":
|
|
1126
|
+
desc = ContinuousDescriptor(
|
|
1127
|
+
name=str(datos[0]),
|
|
1128
|
+
expression=str(datos[1]),
|
|
1129
|
+
meta=False,
|
|
1130
|
+
number_of_decimals=3,
|
|
1131
|
+
)
|
|
1132
|
+
self.addFeature(desc)
|
|
1133
|
+
else:
|
|
1134
|
+
self.Error.transform_error("There is not data input.")
|
|
1135
|
+
|
|
1106
1136
|
def handleNewSignals(self):
|
|
1107
1137
|
if self.data is not None:
|
|
1108
1138
|
self.apply()
|
|
@@ -1188,6 +1218,14 @@ class owtimefeaturesconstructor(OWWidget, ConcurrentWidgetMixin):
|
|
|
1188
1218
|
desc = self._validate_descriptors(desc)
|
|
1189
1219
|
self.start(run, self.data, desc, self.expressions_with_values)
|
|
1190
1220
|
|
|
1221
|
+
'''
|
|
1222
|
+
INTENTO DE PODER USAR VARIABLES NO CONTENIDAS EN EL DATASET ORIGINAL.
|
|
1223
|
+
|
|
1224
|
+
desc = list(self.featuremodel)
|
|
1225
|
+
for d in desc:
|
|
1226
|
+
self.start(run, self.data, self._validate_descriptors(d), self.expressions_with_values)'''
|
|
1227
|
+
|
|
1228
|
+
|
|
1191
1229
|
def on_done(self, result: "Result") -> None:
|
|
1192
1230
|
data, attrs, desc = result.data, result.attributes, result.desc
|
|
1193
1231
|
disc_attrs_not_ok = self.check_attrs_values(
|
|
@@ -1361,6 +1399,7 @@ class Result:
|
|
|
1361
1399
|
def run(data: Table, desc, use_values, task: TaskState) -> Result:
|
|
1362
1400
|
if task.is_interruption_requested():
|
|
1363
1401
|
raise CancelledError # pragma: no cover
|
|
1402
|
+
|
|
1364
1403
|
new_variables, new_metas = construct_variables(desc, data, use_values)
|
|
1365
1404
|
# Explicit cancellation point after `construct_variables` which can
|
|
1366
1405
|
# already run `compute_value`.
|
|
@@ -1374,10 +1413,14 @@ def run(data: Table, desc, use_values, task: TaskState) -> Result:
|
|
|
1374
1413
|
try:
|
|
1375
1414
|
for variable in new_variables:
|
|
1376
1415
|
variable.compute_value.mask_exceptions = False
|
|
1377
|
-
|
|
1416
|
+
try:
|
|
1417
|
+
data = data.transform(new_domain)
|
|
1418
|
+
except:
|
|
1419
|
+
raise TypeError("One or more variables were not found.")
|
|
1378
1420
|
finally:
|
|
1379
1421
|
for variable in new_variables:
|
|
1380
1422
|
variable.compute_value.mask_exceptions = True
|
|
1423
|
+
|
|
1381
1424
|
return Result(data, new_variables, new_metas, desc)
|
|
1382
1425
|
|
|
1383
1426
|
|
|
@@ -1456,6 +1499,7 @@ def validate_exp(exp):
|
|
|
1456
1499
|
|
|
1457
1500
|
def construct_variables(descriptions, data, use_values=False):
|
|
1458
1501
|
# subs
|
|
1502
|
+
|
|
1459
1503
|
variables = []
|
|
1460
1504
|
metas = []
|
|
1461
1505
|
source_vars = data.domain.variables + data.domain.metas
|
|
@@ -17,7 +17,11 @@ from PyQt5.QtWidgets import QPushButton, QVBoxLayout, QHBoxLayout
|
|
|
17
17
|
from orangecontrib.network import Network
|
|
18
18
|
from orangewidget.utils.signals import Input
|
|
19
19
|
|
|
20
|
-
'''
|
|
20
|
+
'''
|
|
21
|
+
|
|
22
|
+
CALCULO DE PONDERACIONES PARA LOS PESOS DE LAS ARISTAS.
|
|
23
|
+
|
|
24
|
+
def calculate_weight(expression):
|
|
21
25
|
# Encontrar todas las coincidencias con las funciones temporales y almacenarlas
|
|
22
26
|
matches_shift = list(re.finditer(r'shift\(([^,]+),([-+]?\d+)\)', expression))
|
|
23
27
|
matches_sum = list(re.finditer(r'sum\(([^,]+),([-+]?\d+),([-+]?\d+)\)', expression))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|