Skip to content

Commit 90d2662

Browse files
committed
fixes to the csv output so it produces CSVs identical to the legacy downloader
1 parent db81470 commit 90d2662

File tree

3 files changed

+89
-68
lines changed

3 files changed

+89
-68
lines changed

simt_emlite/cli/profile_download.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def download_single_day(
119119
serial=downloader.serial,
120120
output_dir=output_dir,
121121
readings=readings_a,
122-
element_marker="A",
122+
element_marker="A" if downloader.is_twin_element else None,
123123
)
124124
print(f"Wrote {len(readings_a)} readings to CSV in {output_dir}")
125125

simt_emlite/smip/smip_csv.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,24 +66,38 @@ def write(
6666

6767
# Write CSV file
6868
with open(full_csv_path, "w", newline="") as csvfile:
69-
writer = csv.writer(csvfile)
69+
writer = csv.writer(csvfile, quoting=csv.QUOTE_ALL)
7070

71-
# Write header
72-
header = ["Timestamp", "Import", "Export"]
73-
if element_marker:
74-
header.append("Element")
71+
# Build column name with optional element marker (e.g., "EML123456789-A")
72+
column_name = f"{serial}-{element_marker}" if element_marker else serial
73+
74+
# Write header with quoted column names
75+
header = ["created_at", column_name, f"{column_name}_rev"]
7576
writer.writerow(header)
7677

77-
# Write records
78+
# Write records (no quoting for data rows)
7879
for record in records:
79-
row = [
80-
record.timestamp.isoformat(),
81-
str(record.import_value) if record.import_value is not None else "",
82-
str(record.export_value) if record.export_value is not None else "",
83-
]
84-
if element_marker:
85-
row.append(element_marker)
86-
writer.writerow(row)
80+
# Format timestamp as "YYYY-MM-DD HH:MM:SS+0000" (no T, no colon in timezone)
81+
ts = record.timestamp
82+
# Format with strftime to get the base, then handle timezone
83+
timestamp_str = ts.strftime("%Y-%m-%d %H:%M:%S%z")
84+
# Remove colon from timezone offset (+00:00 -> +0000)
85+
# strftime %z already gives +0000 format in Python
86+
87+
# Format values as integers (no decimal places)
88+
import_str = (
89+
str(int(record.import_value))
90+
if record.import_value is not None
91+
else ""
92+
)
93+
export_str = (
94+
str(int(record.export_value))
95+
if record.export_value is not None
96+
else ""
97+
)
98+
99+
# Write the raw line directly to avoid quoting data rows
100+
csvfile.write(f"{timestamp_str},{import_str},{export_str}\n")
87101

88102
@staticmethod
89103
def write_from_profile_records(

tests/simt_emlite/smip/test_smip_csv.py

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import unittest
21
import os
2+
import unittest
3+
34
from simt_emlite.smip.smip_csv import SMIPCSV
45
from simt_emlite.smip.smip_csv_record import SMIPCSVRecord
56

6-
class TestSMIPCSV(unittest.TestCase):
77

8+
class TestSMIPCSV(unittest.TestCase):
89
def test_read_smip_file_with_nas(self):
910
"""Test reading SMIP file with NA values (should be skipped)"""
1011
# A number of records with NA values skipped (see unfuddle #410)
11-
test_file = os.path.join(os.path.dirname(__file__), 'test_data', 'EML2207642296-20221013.csv')
12+
test_file = os.path.join(
13+
os.path.dirname(__file__), "test_data", "EML2207642296-20221013.csv"
14+
)
1215

1316
records = SMIPCSV.read_from_file(test_file)
1417

@@ -27,7 +30,9 @@ def test_read_smip_file_with_nas(self):
2730

2831
def test_read_smip_file_all_nas(self):
2932
"""Test reading SMIP file with all NA values (should return empty list)"""
30-
test_file = os.path.join(os.path.dirname(__file__), 'test_data', 'EML2137580799-A-20211210.csv')
33+
test_file = os.path.join(
34+
os.path.dirname(__file__), "test_data", "EML2137580799-A-20211210.csv"
35+
)
3136

3237
records = SMIPCSV.read_from_file(test_file)
3338

@@ -36,7 +41,9 @@ def test_read_smip_file_all_nas(self):
3641

3742
def test_read_smip_file_with_neg1s(self):
3843
"""Test reading SMIP file with -1 values (should be skipped)"""
39-
test_file = os.path.join(os.path.dirname(__file__), 'test_data', 'EML2137580799-A-20211203.csv')
44+
test_file = os.path.join(
45+
os.path.dirname(__file__), "test_data", "EML2137580799-A-20211203.csv"
46+
)
4047

4148
records = SMIPCSV.read_from_file(test_file)
4249

@@ -78,36 +85,35 @@ def test_read_smip_string(self):
7885

7986
def test_write_smip_file(self):
8087
"""Test writing SMIP records to a file"""
81-
import tempfile
8288
import os
89+
import tempfile
8390

8491
# Create test records
8592
from datetime import datetime, timezone
93+
8694
records = [
8795
SMIPCSVRecord(
8896
timestamp=datetime(2023, 12, 10, 14, 30, 0, tzinfo=timezone.utc),
8997
import_value=123.45,
90-
export_value=56.78
98+
export_value=56.78,
9199
),
92100
SMIPCSVRecord(
93101
timestamp=datetime(2023, 12, 10, 15, 0, 0, tzinfo=timezone.utc),
94102
import_value=234.56,
95-
export_value=67.89
103+
export_value=67.89,
96104
),
97105
SMIPCSVRecord(
98106
timestamp=datetime(2023, 12, 10, 15, 30, 0, tzinfo=timezone.utc),
99107
import_value=345.67,
100-
export_value=78.90
101-
)
108+
export_value=78.90,
109+
),
102110
]
103111

104112
# Create temporary directory
105113
with tempfile.TemporaryDirectory() as temp_dir:
106114
# Write records to file
107115
SMIPCSV.write(
108-
serial="EML123456789",
109-
output_file_path=temp_dir,
110-
records=records
116+
serial="EML123456789", output_file_path=temp_dir, records=records
111117
)
112118

113119
# Check that file was created
@@ -116,40 +122,41 @@ def test_write_smip_file(self):
116122
self.assertTrue(os.path.exists(expected_filepath))
117123

118124
# Read the file and verify content
119-
with open(expected_filepath, 'r') as f:
125+
with open(expected_filepath, "r") as f:
120126
content = f.read()
121127

122-
# Check header
123-
self.assertIn("Timestamp,Import,Export", content)
128+
# Check header (new format with quoted column names)
129+
self.assertIn('"created_at","EML123456789","EML123456789_rev"', content)
124130

125-
# Check that all records are present
126-
self.assertIn("2023-12-10T14:30:00+00:00", content)
127-
self.assertIn("2023-12-10T15:00:00+00:00", content)
128-
self.assertIn("2023-12-10T15:30:00+00:00", content)
131+
# Check that all records are present (new timestamp format without T)
132+
self.assertIn("2023-12-10 14:30:00+0000", content)
133+
self.assertIn("2023-12-10 15:00:00+0000", content)
134+
self.assertIn("2023-12-10 15:30:00+0000", content)
129135

130-
# Check that values are present (written as-is in kWh)
131-
self.assertIn("123.45", content)
132-
self.assertIn("234.56", content)
133-
self.assertIn("345.67", content)
136+
# Check that values are present (written as integers)
137+
self.assertIn(",123,", content)
138+
self.assertIn(",234,", content)
139+
self.assertIn(",345,", content)
134140

135141
def test_write_smip_file_with_element_marker(self):
136142
"""Test writing SMIP records with element marker"""
137-
import tempfile
138143
import os
144+
import tempfile
139145

140146
# Create test records
141147
from datetime import datetime, timezone
148+
142149
records = [
143150
SMIPCSVRecord(
144151
timestamp=datetime(2023, 12, 10, 14, 30, 0, tzinfo=timezone.utc),
145152
import_value=123.45,
146-
export_value=56.78
153+
export_value=56.78,
147154
),
148155
SMIPCSVRecord(
149156
timestamp=datetime(2023, 12, 10, 15, 0, 0, tzinfo=timezone.utc),
150157
import_value=234.56,
151-
export_value=67.89
152-
)
158+
export_value=67.89,
159+
),
153160
]
154161

155162
# Create temporary directory
@@ -159,7 +166,7 @@ def test_write_smip_file_with_element_marker(self):
159166
serial="EML123456789",
160167
output_file_path=temp_dir,
161168
records=records,
162-
element_marker="A"
169+
element_marker="A",
163170
)
164171

165172
# Check that file was created with element marker
@@ -168,21 +175,21 @@ def test_write_smip_file_with_element_marker(self):
168175
self.assertTrue(os.path.exists(expected_filepath))
169176

170177
# Read the file and verify content
171-
with open(expected_filepath, 'r') as f:
178+
with open(expected_filepath, "r") as f:
172179
content = f.read()
173180

174-
# Check header includes Element column
175-
self.assertIn("Timestamp,Import,Export,Element", content)
181+
# Check header (new format with serial-element as column names)
182+
self.assertIn('"created_at","EML123456789-A","EML123456789-A_rev"', content)
176183

177-
# Check that element marker is present
178-
self.assertIn("A", content)
184+
# Check that timestamp format is correct (space-separated, no T)
185+
self.assertIn("2023-12-10 14:30:00+0000", content)
179186

180187
def test_write_from_smip_readings(self):
181188
"""Test writing SMIPReading objects to a file"""
182-
import tempfile
183189
import os
184-
190+
import tempfile
185191
from datetime import datetime, timezone
192+
186193
from simt_emlite.smip.smip_reading import SMIPReading
187194

188195
# Create test SMIPReading objects
@@ -193,24 +200,24 @@ def test_write_from_smip_readings(self):
193200
timestamp=datetime(2023, 12, 10, 14, 30, 0, tzinfo=timezone.utc),
194201
imp=123.45,
195202
exp=56.78,
196-
errorCode=0
203+
errorCode=0,
197204
),
198205
SMIPReading(
199206
serial="EML123456789",
200207
register=1,
201208
timestamp=datetime(2023, 12, 10, 15, 0, 0, tzinfo=timezone.utc),
202209
imp=234.56,
203210
exp=67.89,
204-
errorCode=0
211+
errorCode=0,
205212
),
206213
SMIPReading(
207214
serial="EML123456789",
208215
register=1,
209216
timestamp=datetime(2023, 12, 10, 15, 30, 0, tzinfo=timezone.utc),
210217
imp=345.67,
211218
exp=78.90,
212-
errorCode=0
213-
)
219+
errorCode=0,
220+
),
214221
]
215222

216223
# Create temporary directory
@@ -220,7 +227,7 @@ def test_write_from_smip_readings(self):
220227
serial="EML123456789",
221228
output_dir=temp_dir,
222229
readings=readings,
223-
element_marker="A"
230+
element_marker="A",
224231
)
225232

226233
# Check that file was created with element marker
@@ -229,22 +236,22 @@ def test_write_from_smip_readings(self):
229236
self.assertTrue(os.path.exists(expected_filepath))
230237

231238
# Read the file and verify content
232-
with open(expected_filepath, 'r') as f:
239+
with open(expected_filepath, "r") as f:
233240
content = f.read()
234241

235-
# Check header includes Element column
236-
self.assertIn("Timestamp,Import,Export,Element", content)
242+
# Check header (new format with serial-element as column names)
243+
self.assertIn('"created_at","EML123456789-A","EML123456789-A_rev"', content)
237244

238-
# Check that all records are present
239-
self.assertIn("2023-12-10T14:30:00+00:00", content)
240-
self.assertIn("2023-12-10T15:00:00+00:00", content)
241-
self.assertIn("2023-12-10T15:30:00+00:00", content)
245+
# Check that all records are present (new timestamp format without T)
246+
self.assertIn("2023-12-10 14:30:00+0000", content)
247+
self.assertIn("2023-12-10 15:00:00+0000", content)
248+
self.assertIn("2023-12-10 15:30:00+0000", content)
242249

243-
# Check that values are present (written as-is in kWh)
244-
self.assertIn("123.45", content)
245-
self.assertIn("234.56", content)
246-
self.assertIn("345.67", content)
250+
# Check that values are present (written as integers)
251+
self.assertIn(",123,", content)
252+
self.assertIn(",234,", content)
253+
self.assertIn(",345,", content)
247254

248255

249-
if __name__ == '__main__':
256+
if __name__ == "__main__":
250257
unittest.main()

0 commit comments

Comments
 (0)