Merge ~rodrigo-barbieri2010/sftools:sftc into sftools:main

Proposed by Rodrigo Barbieri
Status: Needs review
Proposed branch: ~rodrigo-barbieri2010/sftools:sftc
Merge into: sftools:main
Diff against target: 250 lines (+238/-0)
2 files modified
scripts/sftc_insert (+145/-0)
scripts/sftc_reader (+93/-0)
Reviewer Review Type Date Requested Status
Dan Streetman (community) Needs Fixing
Review via email: mp+426958@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Dan Streetman (ddstreet) wrote :

The 'insert' functionality/method should be added to the main code, not as a script, and then the interface to perform the tc additions should be added to the sf-timecard script

review: Needs Fixing

Unmerged commits

4a94e3e... by Rodrigo Barbieri

Add sftc insert and reader

sftc_insert: inserts a single new timecard according
             to provided params.

sftc_reader: reads timecards definitions from a file
             and inserts them through sftc_insert
             functions.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/scripts/sftc_insert b/scripts/sftc_insert
2new file mode 100755
3index 0000000..5f9d982
4--- /dev/null
5+++ b/scripts/sftc_insert
6@@ -0,0 +1,145 @@
7+#!/usr/bin/python3
8+
9+import logging
10+import pytz
11+import sys
12+
13+from datetime import datetime
14+from pathlib import Path
15+
16+# if called from git source, add parent dir to python path
17+if Path(__file__).parent.name == 'scripts':
18+ sys.path.insert(0, str(Path(__file__).parent.parent))
19+
20+from sftools.argparse import SFObjectArgumentParser
21+from sftools.sf import SF
22+
23+
24+LOG = logging.getLogger(__name__)
25+
26+
27+def parse_params(data, sf=None):
28+
29+ if sf is None:
30+ sf = SF()
31+
32+ # Param Minutes
33+ LOG.debug(f"Minutes Input: {data['minutes']}")
34+ if not isinstance(data['minutes'], int):
35+ LOG.error("Minutes parameter is not an integer")
36+ exit(1)
37+
38+ # Param Case Number
39+ LOG.debug(f"Case Input: {data['case']}")
40+ if not isinstance(data['case'], str):
41+ LOG.error("Case number or ID must be a string")
42+ exit(1)
43+ if len(data['case']) not in (18, 8):
44+ LOG.error("Double check your case number or ID param, it should be "
45+ "either 8 number digits or 18 chars ID")
46+ exit(1)
47+ case = sf.Case(data['case'])
48+ if not case:
49+ LOG.error(f"Case {data['case']} not found")
50+ exit(1)
51+ LOG.debug(f"Case ID: {case.Id}")
52+
53+ # Param Start Time
54+ LOG.debug(f"Start time Input: {data.get('starttime')}")
55+ if data.get('starttime'):
56+ try:
57+ starttime = datetime.strptime(data['starttime'],'%Y-%m-%d-%H-%M')
58+ except ValueError:
59+ LOG.error("Wrong timestamp format for Timecard start time, "
60+ "check your format is 'yyyy-mm-dd-hh-mm'")
61+ exit(1)
62+ starttime = starttime.replace(tzinfo=pytz.UTC)
63+ else:
64+ starttime = datetime.now(pytz.UTC)
65+ starttime_str = starttime.isoformat()
66+ LOG.debug(f"Start time Parsed: {starttime_str}")
67+
68+ # Param Work Performed
69+ LOG.debug(f"Work performed Input: {data['workperformed']}")
70+ if not isinstance(data['case'], str):
71+ LOG.error("Data for 'Work performed' field must be a string")
72+ exit(1)
73+ if len(data['workperformed']) > 254:
74+ LOG.error("Length of 'Work performed' string should not exceed 254 chars")
75+ exit(1)
76+
77+ # Dict
78+ params = {
79+ 'TotalMinutesStatic__c': data['minutes'],
80+ 'CaseId__c': case.Id,
81+ 'StartTime__c': starttime_str,
82+ 'WorkPerformed__c': data['workperformed'],
83+ }
84+ LOG.debug(f"Insert Dict: {params}")
85+ LOG.info(f"Parsed Timecard ( Case Number/ID: {case.CaseNumber} / {case.Id}"
86+ f", Minutes: {data['minutes']}, Start Time: {starttime_str}"
87+ f", Work Performed: {data['workperformed']} )")
88+ return params
89+
90+
91+def insert(params, sf=None):
92+
93+ if sf is None:
94+ sf = SF()
95+
96+ result = sf.TimeCard__c.create(params)
97+ LOG.debug(f"Result: {result}")
98+ if result and result.get('success') == True and result.get('errors') == []:
99+ LOG.info(f"Inserted Timecard Successfully ( "
100+ f"Case ID: {params['CaseId__c']}, "
101+ f"Minutes: {params['TotalMinutesStatic__c']}, "
102+ f"Start Time: {params['StartTime__c']}, "
103+ f"Work Performed: {params['WorkPerformed__c']} )")
104+ else:
105+ LOG.error(f"Failed to insert Timecard ( "
106+ f"Case ID: {params['CaseId__c']}, "
107+ f"Minutes: {params['TotalMinutesStatic__c']}, "
108+ f"Start Time: {params['StartTime__c']}, "
109+ f"Work Performed: {params['WorkPerformed__c']} )"
110+ f" : Response: {result}")
111+
112+
113+def main():
114+
115+ parser = SFObjectArgumentParser()
116+
117+ parser.add_argument('-m', '--minutes', required=True, type=int,
118+ help='Timecard minutes')
119+ parser.add_argument('-c', '--case', required=True,
120+ help='Case number (or Case ID)')
121+ parser.add_argument('-w', '--workperformed', required=True,
122+ help='Work performed (Timecard description)')
123+ parser.add_argument('-t', '--starttime', required=False, type=str,
124+ help='Start time of timecard '
125+ '(Format: yyyy-mm-dd-hh-mm) in UTC. If not '
126+ 'specified, defaults to current timestamp')
127+
128+ opts = parser.parse_args()
129+
130+ logging.basicConfig(level='DEBUG' if opts.verbose else 'INFO',
131+ format='%(message)s')
132+
133+ sf = opts.functions.SF()
134+ data = {
135+ 'minutes': opts.minutes,
136+ 'case': opts.case,
137+ 'workperformed': opts.workperformed,
138+ 'starttime': opts.starttime,
139+ }
140+
141+ params = parse_params(data, sf=sf)
142+
143+ if opts.dry_run:
144+ LOG.info("Dry run completed")
145+ exit(0)
146+
147+ insert(params, sf=sf)
148+
149+
150+if __name__ == "__main__":
151+ main()
152diff --git a/scripts/sftc_reader b/scripts/sftc_reader
153new file mode 100755
154index 0000000..c503176
155--- /dev/null
156+++ b/scripts/sftc_reader
157@@ -0,0 +1,93 @@
158+#!/usr/bin/python3
159+
160+from sftc_insert import parse_params, insert
161+import argparse
162+import logging
163+import sys
164+
165+from pathlib import Path
166+
167+# if called from git source, add parent dir to python path
168+if Path(__file__).parent.name == 'scripts':
169+ sys.path.insert(0, str(Path(__file__).parent.parent))
170+
171+from sftools.sf import SF
172+
173+LOG = logging.getLogger(__name__)
174+
175+
176+def read(filename, translation_dict=None):
177+
178+ if translation_dict is None:
179+ translation_dict = {}
180+
181+ with open(filename, 'r') as file:
182+ lines = file.readlines()
183+
184+ timecards = []
185+
186+ for line in lines:
187+ if not line.strip():
188+ continue
189+ contents = line.split('===')
190+ if len(contents) < 3:
191+ LOG.error("Incorrect format, please use: "
192+ "'case === minutes === workperformed'")
193+ case = contents[0].strip()
194+ case = translation_dict.get(case) or case
195+ minutes = int(contents[1].strip())
196+ workperformed = contents[2].strip()
197+ starttime = None
198+ if len(contents) > 3:
199+ starttime = contents[3].strip()
200+ data = {
201+ 'case': case,
202+ 'minutes': minutes,
203+ 'workperformed': workperformed,
204+ 'starttime': starttime,
205+ }
206+ timecards.append(data)
207+
208+ LOG.debug(f"Timecard data parsed from file {timecards}")
209+ return timecards
210+
211+
212+def main(translation_dict=None, sf=None):
213+
214+ if translation_dict is None:
215+ translation_dict = {}
216+
217+ parser = argparse.ArgumentParser()
218+ parser.add_argument('--dry-run', action='store_true',
219+ help="Dry-run only, do not make any changes")
220+ parser.add_argument('-f', '--filename', type=str, default="timecards.txt",
221+ help="File name to open and parse timecards to "
222+ "insert. Default is timecards.txt")
223+ parser.add_argument('-v', '--verbose', action='store_true', default=False,
224+ help="Enable verbose logging")
225+
226+ opts = parser.parse_args()
227+
228+ logging.basicConfig(level='DEBUG' if opts.verbose else 'INFO',
229+ format='%(message)s')
230+
231+ timecards = read(opts.filename, translation_dict=translation_dict)
232+
233+ if sf is None:
234+ sf = SF()
235+
236+ req_params = []
237+ for timecard in timecards:
238+ params = parse_params(timecard, sf=sf)
239+ req_params.append(params)
240+
241+ if opts.dry_run:
242+ LOG.info("Dry run completed")
243+ exit(0)
244+
245+ for params in req_params:
246+ insert(params, sf=sf)
247+
248+
249+if __name__ == "__main__":
250+ main()

Subscribers

People subscribed via source and target branches