Skip to content

Commit

Permalink
Validation version alerts rownums (#25)
Browse files Browse the repository at this point in the history
* Digital contact validation now working.

* increment semver.

* Migrations removing column 'number'

* We are not actually ignoring the 'number' column.

* Moved all sizes to use dp for screen scaling.

* adding workflow dispatch run option.

* Syntax fix for workflow dispatch

* Syntax fix for workflow dispatch

* Not sure why it's complaining about line 18

* Still complaining about line 18

* Removing unused conditional

* d710 now passing spec.

* Verified chirp and d710 passing spec.

* Fixed an issue with channel numbers in dmr_ids. Still more work to do.

* Removed more dictionary references.

* Added d878 test.

* trim whitespace.

* Successfully removed 'number' column.

* Successfully removed 'number' column.

* Fixed failing unit test for d878

* Syntax env issue.

* Moving echo command to different section.

* Had the wrong string in the version file, that's why it wasn't updating.

* Let's try updating this again.

* Try putting it in run tests.

* only concerned with pc at the moment.

* Version update check?

* woops it's an environment variable.

* Bad character.

* Version echo in its own place.

* File pathing tweak.

* Are we even rewriting the file?

* Added version to title bar.

* Optimize imports, add version output.

* Updating steps so we can get a passing build.

* Try piping output.

* Set version back to development.

* Forgot out-file encoding.

* Let's see if we can get mac and pc working.

* Updated syntax to fix error.

* local yml complains about syntax.

* Still not happy about syntax.

* not happy with double quotes.

* Fixed op performing check.
  • Loading branch information
n2qzshce authored Apr 3, 2021
1 parent fd939c6 commit b57f701
Show file tree
Hide file tree
Showing 39 changed files with 838 additions and 167 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/pyinstaller-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
name: Build executeable

env:
semver: 1.6.0.${{ github.run_number }}
semver: 1.6.1.${{ github.run_number }}
python-version: 3.8
KIVY_GL_BACKEND: 'angle_sdl2'

Expand All @@ -27,10 +27,12 @@ jobs:
exe-extension: .exe
short-name: win
move-command: move
version-command: ${{ '|' }} out-file -encoding utf-8 ./src/radio_sync_version.py
- os: macos-10.15
exe-extension: .app
short-name: osx
move-command: mv
version-command: ${{ '>' }} ./src/radio_sync_version.py
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ env.python-version }}
Expand All @@ -41,6 +43,9 @@ jobs:
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
- name: Write version file
run: |
echo "version = '${{ env.semver }}'" ${{ matrix.version-command }}
- name: Run Tests
run: |
python -m unittest discover -v
Expand Down
30 changes: 29 additions & 1 deletion radio_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import sys

import src.ham.util.radio_types
from src import radio_sync_version
from src.ham.migration.migration_manager import MigrationManager
from src.ham.radio_generator import RadioGenerator
from src.ham.wizard import Wizard
Expand Down Expand Up @@ -32,6 +33,14 @@ def main():
help='Destroys `in` and `out` directories along with all their contents.',
)

parser.add_argument(
'--migrate-check',
action='store_true',
default=False,
required=False,
help='Checks for outdated columns.',
)

parser.add_argument(
'--migrate', '-m',
action='store_true',
Expand All @@ -41,7 +50,7 @@ def main():
)

parser.add_argument(
'--migrate_cleanup',
'--migrate-cleanup',
action='store_true',
default=False,
required=False,
Expand Down Expand Up @@ -71,6 +80,14 @@ def main():
help=f"""Target radios to create."""
)

parser.add_argument(
'--version',
action='store_true',
default=False,
required=False,
help='Display app version.',
)

parser.add_argument(
'--debug',
action='store_true',
Expand All @@ -85,6 +102,7 @@ def main():

if arg_values.debug:
logger.setLevel(logging.DEBUG)
logging.debug("Logging level set to debug.")

if arg_values.force:
logging.warning("FORCE HAS BEEN SET. ALL PROMPTS WILL DEFAULT YES. Files may be destroyed.")
Expand All @@ -111,6 +129,12 @@ def main():
wizard.bootstrap(arg_values.force)
op_performed = True

if arg_values.migrate_check:
logging.info("Running migration check")
migrations = MigrationManager()
migrations.log_check_migrations()
op_performed = True

if arg_values.migrate:
logging.info("Running migration")
migrations = MigrationManager()
Expand All @@ -123,6 +147,10 @@ def main():
migrations.remove_backups()
op_performed = True

if arg_values.version:
logging.info(f"App version {src.radio_sync_version.version}")
op_performed = True

if len(arg_values.radios) > 0:
logging.info("Running radio generator.")
radio_generator = RadioGenerator(arg_values.radios)
Expand Down
92 changes: 92 additions & 0 deletions src/ham/migration/migration_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import re
import shutil

from src.ham.radio.default_radio.dmr_contact_default import DmrContactDefault
from src.ham.radio.default_radio.radio_channel_default import RadioChannelDefault
from src.ham.radio.default_radio.radio_zone_default import RadioZoneDefault
from src.ham.util.file_util import FileUtil


Expand Down Expand Up @@ -48,6 +51,32 @@ def _add_col(self, file_name, col_name, default_val):
os.rename(f'{file_name}.tmp', f'{file_name}')
return

def _delete_col(self, file_name, col_name):
logging.info(f'Deleting column `{col_name}` in `{file_name}`')
reader = FileUtil.open_file(f'{file_name}', 'r')
cols = reader.readline().replace('\n', '').split(',')
if cols == ['']:
cols = []
if col_name in cols:
cols.remove(col_name)
reader.seek(0)

writer = FileUtil.open_file(f'{file_name}.tmp', 'w+')
dict_writer = csv.DictWriter(writer, fieldnames=cols, dialect='unix', quoting=0)
dict_reader = csv.DictReader(reader, fieldnames=cols)

dict_writer.writeheader()
for row in dict_reader:
if dict_reader.line_num == 1:
continue
dict_writer.writerow(row)

reader.close()
writer.close()
os.remove(file_name)
os.rename(f'{file_name}.tmp', f'{file_name}')
return

def remove_backups(self):
if not os.path.exists('in/'):
return
Expand All @@ -58,6 +87,61 @@ def remove_backups(self):
os.remove(f'in/{file_name}')
return

def check_migrations_needed(self):
not_needed_cols = dict()

channels_file = 'in/input.csv'
channel_cols = dict(RadioChannelDefault.create_empty().__dict__)
extra_cols = self._migration_check(channels_file, channel_cols)

if len(extra_cols) > 0:
not_needed_cols['input.csv'] = extra_cols

contacts_file = 'in/digital_contacts.csv'
contact_cols = dict(DmrContactDefault.create_empty().__dict__)
extra_cols = self._migration_check(contacts_file, contact_cols)

if len(extra_cols) > 0:
not_needed_cols['digital_contacts.csv'] = extra_cols

zones_file = 'in/zones.csv'
zone_cols = dict(RadioZoneDefault.create_empty().__dict__)
extra_cols = self._migration_check(zones_file, zone_cols)

if len(extra_cols) > 0:
not_needed_cols['zones.csv'] = extra_cols

return not_needed_cols

def log_check_migrations(self):
try:
results = self.check_migrations_needed()
except Exception:
logging.info("Migrations check could not be run. Have you run the setup wizard?")
return

output_str = ""
for k in results.keys():
for v in results[k]:
output_str += f"{k:20s} | {v}\n"
if len(output_str) != 0:
logging.info(f"The following extra columns were found: \n{'file name':20s} | extra column\n{output_str}")
logging.info(f"New columns may still be needed. Create radio plugs to validate and create.")

def _migration_check(self, input_file, needed_cols):
f = FileUtil.open_file(input_file, 'r')
dict_reader = csv.DictReader(f)
provided_fields = dict_reader.fieldnames
f.close()

needed_fields = needed_cols.keys()

not_needed = []
for provided in provided_fields:
if provided not in needed_fields:
not_needed.append(provided)
return not_needed

def migrate(self):
existing_backups = False
files_list = []
Expand All @@ -76,6 +160,7 @@ def migrate(self):
self._migrate_one()
self._migrate_two()
self._migrate_three()
self._migrate_four()
logging.info("Migrations are complete. Your original files have been renamed to have a `.bak` extension.")

def _migrate_one(self):
Expand Down Expand Up @@ -126,3 +211,10 @@ def _migrate_three(self):
zone_columns = ['number', 'name']
self._add_cols_to_file('in/zones.csv', zone_columns)
return

def _migrate_four(self):
logging.info("Running migration step 4: removing 'number' columns")
self._delete_col('in/input.csv', 'number')
self._delete_col('in/dmr_id.csv', 'number')
self._delete_col('in/digital_contacts.csv', 'number')
return
3 changes: 1 addition & 2 deletions src/ham/radio/chirp/radio_channel_chirp.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
class RadioChannelChirp(RadioChannel):
def __init__(self, cols, digital_contacts, dmr_ids):
super().__init__(cols, digital_contacts, dmr_ids)
self.number.set_alias(radio_types.CHIRP, 'Location')
self.short_name.set_alias(radio_types.CHIRP, 'Name')
self.rx_freq.set_alias(radio_types.CHIRP, 'Frequency')
self.rx_ctcss.set_alias(radio_types.CHIRP, 'cToneFreq')
Expand All @@ -18,7 +17,7 @@ def skip_radio_csv(self):

def headers(self):
output = list()
output.append(f"{self.number.get_alias(radio_types.CHIRP)}")
output.append(f"Location")
output.append(f"{self.short_name.get_alias(radio_types.CHIRP)}")
output.append(f"{self.rx_freq.get_alias(radio_types.CHIRP)}")
output.append(f"Duplex")
Expand Down
13 changes: 6 additions & 7 deletions src/ham/radio/cs800/dmr_contact_cs800.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@
class DmrContactCs800(DmrContact):
def __init__(self, cols):
super().__init__(cols)
self.number.set_alias(radio_types.CS800, 'No')
self.radio_id.set_alias(radio_types.CS800, 'Call ID')
self.digital_id.set_alias(radio_types.CS800, 'Call ID')
self.name.set_alias(radio_types.CS800, 'Call Alias')
self.call_type.set_alias(radio_types.CS800, 'Call Type')
return

def headers(self):
output = list()
output.append(f'{self.number.get_alias(radio_types.CS800)}')
output.append(f'No')
output.append(f'{self.name.get_alias(radio_types.CS800)}')
output.append(f'{self.call_type.get_alias(radio_types.CS800)}')
output.append(f'{self.radio_id.get_alias(radio_types.CS800)}')
output.append(f'{self.digital_id.get_alias(radio_types.CS800)}')
output.append(f'Receive Tone')
return output

def output(self):
def output(self, number):
output = list()
output.append(f'{self.number.fmt_val()}')
output.append(f'{number}')
output.append(f'{self.name.fmt_val()}')
output.append(f'Group Call')
output.append(f'{self.radio_id.fmt_val()}')
output.append(f'{self.digital_id.fmt_val()}')
output.append(f'No')
return output
11 changes: 5 additions & 6 deletions src/ham/radio/cs800/dmr_user_cs800.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@


class DmrUserCs800(DmrUser):
def __init__(self, cols, number=None):
super().__init__(cols, number=number)
self.number.set_alias(radio_types.CS800, 'No')
def __init__(self, cols):
super().__init__(cols)
self.callsign.set_alias(radio_types.CS800, 'Call Alias')
self.radio_id.set_alias(radio_types.CS800, 'Call ID')

def headers(self):
output = list()
output.append(f'{self.number.get_alias(radio_types.CS800)}')
output.append(f'No')
output.append(f'{self.callsign.get_alias(radio_types.CS800)}')
output.append(f'Call Type')
output.append(f'{self.radio_id.get_alias(radio_types.CS800)}')
output.append(f'Receive Tone')
return output

def output(self):
def output(self, number):
output = list()
output.append(f'{self.number.fmt_val()}')
output.append(f'{number}')
output.append(f'{self.callsign.fmt_val()} {self.first_name.fmt_val()} {self.last_name.fmt_val("")[:1]}')
output.append(f'Private Call')
output.append(f'{self.radio_id.fmt_val()}')
Expand Down
16 changes: 7 additions & 9 deletions src/ham/radio/cs800/radio_additional_cs800.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ def _output_channels(self):

analog_num = 1
digital_num = 1
for radio_channel in self._channels.values():
for radio_channel in self._channels:
casted_channel = RadioChannelBuilder.casted(radio_channel, radio_types.CS800)

if casted_channel.is_digital():
digital_sheet.append(casted_channel.output(digital_num))
digital_sheet.append(casted_channel.output(None))
digital_num += 1
else:
analog_sheet.append(casted_channel.output(analog_num))
analog_sheet.append(casted_channel.output(None))
analog_num += 1
channels_workbook.save(f'out/{self._style}/{self._style}_channels.xlsx')
channels_workbook.close()
Expand All @@ -61,21 +61,19 @@ def _output_user(self):
casted_contact = DmrContactBuilder.casted(dmr_contact, self._style)
if casted_contact.name.fmt_val() == 'Analog':
continue
casted_contact.number._fmt_val = number
dmr_contacts_sheet.append(casted_contact.output())
dmr_contacts_sheet.append(casted_contact.output(None))
number += 1

logging.info(f"Writing DMR users for {self._style}")
for dmr_user in self._users.values():
dmr_user.number._fmt_val = number
casted_user = DmrUserBuilder.casted(dmr_user.cols, number, self._style)
dmr_contacts_sheet.append(casted_user.output())
casted_user = DmrUserBuilder.casted(dmr_user.cols, self._style)
dmr_contacts_sheet.append(casted_user.output(number))
number += 1
logging.debug(f"Writing user row {number}")
if number % file_util.USER_LINE_LOG_INTERVAL == 0:
logging.info(f"Writing user row {number}")

logging.info("Saving workbook...")
logging.info(f"Saving {self._style} workbook...")
user_workbook.save(f'out/{self._style}/{self._style}_user.xlsx')
logging.info("Save done.")
user_workbook.close()
Expand Down
7 changes: 3 additions & 4 deletions src/ham/radio/cs800/radio_channel_cs800.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
class RadioChannelCS800(RadioChannel):
def __init__(self, cols, digital_contacts, dmr_ids):
super().__init__(cols, digital_contacts, dmr_ids)
self.number.set_alias(radio_types.CS800, 'No')
self.name.set_alias(radio_types.CS800, 'Channel Alias')
self.rx_freq.set_alias(radio_types.CS800, 'Receive Frequency')
self.digital_timeslot.set_alias(radio_types.CS800, 'Time Slot')
Expand All @@ -23,7 +22,7 @@ def headers(self):

def _headers_cs800_digital(self):
output = list()
output.append(self.number.get_alias(radio_types.CS800)) # No
output.append('No') # No
output.append(self.name.get_alias(radio_types.CS800)) # Channel Alias
output.append('Digital Id') # Digital Id
output.append(self.digital_color.get_alias(radio_types.CS800)) # Color Code
Expand Down Expand Up @@ -57,7 +56,7 @@ def _headers_cs800_digital(self):

def _headers_cs800_analog(self):
output = list()
output.append(f'{self.number.get_alias(radio_types.CS800)}') # No
output.append(f'No') # No
output.append(f'{self.name.get_alias(radio_types.CS800)}') # Channel Alias
output.append(f'Squelch Level') # Squelch Level
output.append(f'Channel Band[KHz]') # Channel Band[KHz]
Expand Down Expand Up @@ -120,7 +119,7 @@ def _output_cs800_digital(self, channel_number):
output.append(f'Off') # Emergency Call Indication
output.append(f'{transmit_frequency:.4f}') # Transmit Frequency
output.append(f'Middle') # TX Ref Frequency
output.append(f'{self.digital_contacts[self.digital_contact.fmt_val()].name.fmt_val()}') # TX Contact
output.append(f'{self.digital_contacts[self.digital_contact_id.fmt_val()].name.fmt_val()}') # TX Contact
output.append(f'None') # Emergency System
output.append(f'{self.tx_power.fmt_val()}') # Power Level
output.append(f'Color Code') # TX Admit
Expand Down
Loading

0 comments on commit b57f701

Please sign in to comment.