Skip to content
Snippets Groups Projects
Commit 144d0bb4 authored by Anders H. Rebner's avatar Anders H. Rebner
Browse files

#38 #39 Opprydding og dokumentering

parent c921ea52
No related branches found
No related tags found
1 merge request!40#38 #39 Eksporter og importer SOI til/fra fil
Pipeline #75982 failed
......@@ -45,3 +45,7 @@ __pycache__/
# Generated codebook PDF-files
Kodebok_*.pdf
# Generated SOI-files
SOI_*_*_*_*.txt
SOI_*_*_*_*.json
\ No newline at end of file
......@@ -5,8 +5,6 @@ Built up by widgets implemented in other modules.
"""
import sys
import os
import json
from datetime import datetime
from enum import Enum
from functools import partial
from PySide2.QtWidgets import (
......@@ -22,15 +20,15 @@ from PySide2.QtWidgets import (
from PySide2.QtGui import QIcon
from PySide2.QtCore import QTimer
from soitool.soi_workspace_widget import SOIWorkspaceWidget
from soitool.soi import SOI
from soitool.codebook import CodeBookTableView
from soitool.codebook_row_adder import CodebookRowAdder
from soitool.codebook_to_pdf import generate_codebook_pdf
from soitool.soi_export import serialize_soi
from soitool.compressor import compress, decompress
from soitool.dialog_wrappers import exec_info_dialog
from soitool.database import Database, DBPATH
from soitool.modules.module_table import TableModule
from soitool.database import Database
from soitool.serialize_export_import_soi import (
export_soi,
import_soi,
)
class ModuleType(Enum):
......@@ -78,6 +76,8 @@ class MainWindow(QMainWindow):
filepath = os.path.join(dirname, filename)
self.setWindowIcon(QIcon(filepath))
# pylint: disable=R0914, R0915
# Ignoring "Too manu local variables" and "Too many statements"
def setup_menubar(self):
"""Set up menubar with submenus and actions."""
menu = self.menuBar()
......@@ -86,7 +86,7 @@ class MainWindow(QMainWindow):
help_menu = menu.addMenu("Hjelp")
# New SOI
new_soi = QAction("Ny SOI", self)
new_soi = QAction("Ny", self)
new_soi.setShortcut("Ctrl+n")
new_soi.setStatusTip("Opprett en ny SOI")
file_menu.addAction(new_soi)
......@@ -117,12 +117,24 @@ class MainWindow(QMainWindow):
save_soi.setStatusTip("Lagre SOI i databasen")
file_menu.addAction(save_soi)
# Export
export = QAction("Eksporter", self)
export.setShortcut("Ctrl+e")
export.setStatusTip("Eksporter SOI til annet filformat")
export.triggered.connect(partial(self.export_soi, compressed=True))
file_menu.addAction(export)
# Export SOI
export_serialized_soi = file_menu.addMenu("Eksporter")
# Compressed SOI
export_compressed = QAction("Komprimert", self)
export_compressed.setShortcut("Ctrl+e")
export_compressed.setStatusTip("Eksporter komprimert SOI")
export_compressed.triggered.connect(
partial(self.try_export_soi, compressed=True)
)
# Uncompressed SOI
export_uncompressed = QAction("Ukomprimert", self)
export_uncompressed.setStatusTip("Eksporter ukomprimert SOI")
export_uncompressed.triggered.connect(
partial(self.try_export_soi, compressed=False)
)
export_serialized_soi.addAction(export_uncompressed)
export_serialized_soi.addAction(export_compressed)
file_menu.addMenu(export_serialized_soi)
# View/edit Codebook
codebook = QAction("Se/rediger kodebok", self)
......@@ -192,29 +204,22 @@ class MainWindow(QMainWindow):
self.tabs.addTab(tab, "Kodebok")
self.tabs.setCurrentWidget(tab)
def export_soi(self, compressed=True):
"""Export SOI if current tab is SOIWorkspaceWidget.
def try_export_soi(self, compressed=True):
"""Export the SOI in the current tab.
Feedback is given through a dialog if the current tab does not show a
SOI (tab is not a SOIWorkspaceWidget).
Give feedback to user if tab is not SOIWorkspaceWidget.
Parameters
----------
compressed : bool, optional
Serialized SOI is compressed if True (default)
"""
tab_widget = self.tabs.currentWidget()
# If tab contains a SOI
if isinstance(tab_widget, SOIWorkspaceWidget):
serialized = serialize_soi(tab_widget.soi)
title = tab_widget.soi.title
date = datetime.now().strftime("%Y_%m_%d")
file_name = f"SOI_{title}_{date}"
if compressed:
serialized = compress(serialized)
file = open(file_name + ".txt", "w")
else:
file = open(file_name + ".json", "w")
file.write(str(serialized))
file.close()
export_soi(tab_widget.soi, compressed)
else:
exec_info_dialog(
"Valgt tab er ingen SOI-tab",
......@@ -223,89 +228,31 @@ class MainWindow(QMainWindow):
)
def import_soi(self):
"""Import uncompressed or compressed soi.
"""Import serialized SOI.
Launches a QFileDialog with .txt and .json as accepted file formats.
Reads content and decompresses if necessary, .txt-files are compressed.
Opens and selects new tab with the imported SOI.
Launches a QFileDialog with .txt and .json as accepted file extensions.
A SOIWorkspaceWidget containing the SOI-object is created and opened
in a new tab, which is selected.
"""
# Get file-name from dialog
file_name = QFileDialog().getOpenFileName(
self,
"Open SOI",
"Åpne SOI",
os.getcwd(),
"Text/JSON-Files (SOI_*.txt SOI_*.json)",
"Text/JSON-filer (SOI_*.txt SOI_*.json)",
)[0]
if len(file_name) > 0:
soi = import_soi(file_name)
with open(file_name, "r") as file:
if file_name[-4::] == ".txt":
serialized = eval(decompress(file.read()))
else:
serialized = eval(file.read())
modules = []
for module in serialized["modules"]:
module_type = module["type"]
if module_type == "TableModule":
size = module["data"]["size"]
content = module["data"]["content"]
meta = module["meta"]
modules.append(
{"widget": TableModule(size, content), "meta": meta}
)
else:
raise ValueError
# attachments
attachments = []
for attachment in serialized["attachments"]:
module_type = attachment["type"]
if module_type == "TableModule":
size = module["data"]["size"]
content = module["data"]["content"]
meta = module["meta"]
attachments.append(
{"widget": TableModule(size, content), "meta": meta}
)
else:
raise ValueError
# ÅPNE SOI I TAB
soi = SOI(
serialized["title"],
serialized["description"],
"1", # version
None, # date
serialized["valid"]["from"],
serialized["valid"]["to"],
serialized["icon"],
serialized["classification"],
serialized["orientation"],
serialized["placement_strategy"],
serialized["algorithm_bin"],
serialized["algorithm_pack"],
serialized["algorithm_sort"],
modules,
attachments,
)
# Create SOIWorkspaceWidget
tab = SOIWorkspaceWidget(soi)
self.tabs.addTab(tab, soi.title)
self.tabs.setCurrentWidget(tab)
# SOI(modules=.., attachments=...)
# Create and select tab
tab = SOIWorkspaceWidget(soi)
self.tabs.addTab(tab, soi.title)
self.tabs.setCurrentWidget(tab)
if __name__ == "__main__":
# Create and set up database if it does not exist
if not os.path.exists(DBPATH):
Database()
app = QApplication(sys.argv)
WINDOW = MainWindow()
WINDOW.showMaximized()
......
......@@ -8,8 +8,8 @@ HEADER_FONT.setFamily("Arial")
HEADER_FONT.setPointSize(12)
HEADER_FONT.setWeight(100)
START_COLUMNS = 2
START_ROWS = 2
START_COLUMNS = 2
class Meta(type(ModuleBase), type(QTableWidget)):
......@@ -25,17 +25,12 @@ class TableModule(ModuleBase, QTableWidget, metaclass=Meta):
The widget does not use more room than needed, and resizes dynamically.
Columnheaders are styled with light grey background and bold text.
Has shortcuts for adding and removing rows and columns.
"""
def set_pos(self, pos):
"""Set position of widget.
Parameters
----------
pos : QPoint
Position (x, y).
"""
self.move(pos)
By default, the widget initializes as an empty START_ROWS * START_COLUMNS
table. If parameters are given, the table initializes accordingly:
'size' is a dict: {"width": int, "height": int},
'content' is a 2D list where content[x][y] represents row x, column y.
"""
def __init__(self, size=None, content=None):
self.type = "TableModule"
......@@ -47,37 +42,38 @@ class TableModule(ModuleBase, QTableWidget, metaclass=Meta):
self.horizontalHeader().hide()
self.verticalHeader().hide()
# If parameters are None, start as empty table.
if size is None and content is None:
print("size og content is None")
# Set number of columns and rows
self.setColumnCount(START_COLUMNS)
self.setRowCount(START_ROWS)
# Resize width and height of columns and rows, and set size of window
# Resize width and height of columns and rows, & set size of window
self.resize()
self.setFixedWidth(START_COLUMNS * self.columnWidth(0) + 2)
self.setFixedHeight(START_ROWS * self.rowHeight(0) + 5)
# Set headeritems
# Set header-items
for i in range(self.columnCount()):
self.set_header_item(i, "")
else:
# print("size og content er noe")
# print("columncount: ", len(content[0]))
# print("rowcount: ", len(content))
self.setColumnCount(len(content[0]))
self.setRowCount(len(content))
# Set headeritems
# Set header-items
for i in range(self.columnCount()):
self.set_header_item(i, content[0][i])
# Set cell-items
for i in range(1, self.rowCount()):
for j in range(self.columnCount()):
item = QTableWidgetItem(content[i][j])
self.setItem(i, j, item)
self.resize()
# self.setFixedWidth(size["width"])
# self.setFixedHeight(size["height"])
self.resizeColumnsToContents()
self.resizeRowsToContents()
self.setFixedWidth(size["width"])
self.setFixedHeight(size["height"])
# Remove scrollbars
self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
......@@ -200,6 +196,16 @@ class TableModule(ModuleBase, QTableWidget, metaclass=Meta):
return width, height
def set_pos(self, pos):
"""Set position of widget.
Parameters
----------
pos : QPoint
Position (x, y).
"""
self.move(pos)
def render_onto_pdf(self):
"""Render onto pdf."""
......
"""Includes functionality for serializing, exporting and importing SOI."""
import json
from datetime import datetime
from ast import literal_eval
from soitool.soi import SOI
from soitool.compressor import compress, decompress
from soitool.modules.module_table import TableModule
def serialize_soi(soi):
"""Serialize SOI to JSON-string.
Parameters
----------
soi : soitool.soi.SOI
SOI to serialize.
Returns
-------
String
JSON-string containing all SOI-information.
Raises
------
ValueError
Raises error if parameter 'soi' is not a SOI.
"""
# If parameter 'soi' is not a SOI
if not isinstance(soi, SOI):
raise ValueError(
"Invalid value for parameter 'soi': " + "'{}'.".format(soi)
)
# Create dict with relevant module-information
modules = []
for module in soi.modules:
modules.append(
{
"type": module["widget"].type,
"data": module["widget"].get_as_dict(),
"meta": module["meta"],
}
)
# Create dict with relevant attachment-information
attachments = []
for attachment in soi.attachments:
attachments.append(
{
"type": attachment["widget"].type,
"data": attachment["widget"].get_as_dict(),
"meta": attachment["meta"],
}
)
# Create dict with all relevant SOI-information
serialized = {
"title": soi.title,
"description": soi.description,
"date": soi.date,
"valid": {"from": soi.valid_from, "to": soi.valid_to},
"icon": soi.icon,
"classification": soi.classification,
"orientation": soi.orientation,
"placement_strategy": soi.placement_strategy,
"algorithm_bin": soi.algorithm_bin,
"algorithm_pack": soi.algorithm_pack,
"algorithm_sort": soi.algorithm_sort,
"modules": modules,
"attachments": attachments,
}
return json.dumps(serialized)
def export_soi(soi, compressed=True):
"""Export SOI.
A .txt-file is created to contain compressed SOI.
A .json-file is created to contain uncompressed SOI.
The generated file-name will be on the format: "SOI_title_YYYY_mm_dd",
where title is the soi-title.
Parameters
----------
soi: soitool.soi.SOI
SOI to export
compressed : bool, optional
Serialized SOI is compressed if True (default).
"""
# Serialize SOI
serialized = serialize_soi(soi)
# Generate filename
title = soi.title
date = datetime.now().strftime("%Y_%m_%d")
file_name = f"SOI_{title}_{date}"
if compressed:
serialized = compress(serialized)
file = open(file_name + ".txt", "w")
else:
file = open(file_name + ".json", "w")
file.write(str(serialized))
file.close()
def import_soi(file_name):
"""Import compressed or uncompressed serialized SOI.
Reads content of file and decompresses it for .txt-files.
Creates an SOI-object based on the file content.
"""
with open(file_name, "r") as file:
if file_name[-4::] == ".txt":
serialized = literal_eval(decompress(file.read()))
else:
serialized = literal_eval(file.read())
# Create dict for modules with modified format
modules = []
for module in serialized["modules"]:
module_type = module["type"]
if module_type == "TableModule":
size = module["data"]["size"]
content = module["data"]["content"]
meta = module["meta"]
modules.append(
{"widget": TableModule(size, content), "meta": meta}
)
else:
raise ValueError(
"Module-type '{}' is not recognized.".format(module_type)
)
# Create dict for attachments with modified format
attachments = []
for attachment in serialized["attachments"]:
module_type = attachment["type"]
if module_type == "TableModule":
size = attachment["data"]["size"]
content = attachment["data"]["content"]
meta = attachment["meta"]
attachments.append(
{"widget": TableModule(size, content), "meta": meta}
)
else:
raise ValueError(
"Module-type '{}' is not recognized.".format(module_type)
)
# Create SOI
soi = SOI(
serialized["title"],
serialized["description"],
"1", # version
None, # date
serialized["valid"]["from"],
serialized["valid"]["to"],
serialized["icon"],
serialized["classification"],
serialized["orientation"],
serialized["placement_strategy"],
serialized["algorithm_bin"],
serialized["algorithm_pack"],
serialized["algorithm_sort"],
modules,
attachments,
)
return soi
"""Includes functionality for serializing SOI as a Dictionary."""
import json
from soitool.soi import SOI
def serialize_soi(soi):
"""Export SOI to JSON(compressed?)."""
print("Exporting")
if not isinstance(soi, SOI):
raise ValueError(
"Invalid value for parameter 'soi': " + "'{}'".format(soi)
)
modules = []
for module in soi.modules:
modules.append(
{
"type": module["widget"].type,
"data": module["widget"].get_as_dict(),
"meta": module["meta"],
}
)
attachments = []
for attachment in soi.attachments:
attachments.append(
{
"type": attachment["widget"].type,
"data": attachment["widget"].get_as_dict(),
"meta": attachment["meta"],
}
)
serialized = {
"title": soi.title,
"description": soi.description,
"date": soi.date,
"valid": {"from": soi.valid_from, "to": soi.valid_to},
"icon": soi.icon,
"classification": soi.classification,
"orientation": soi.orientation,
"placement_strategy": soi.placement_strategy,
"algorithm_bin": soi.algorithm_bin,
"algorithm_pack": soi.algorithm_pack,
"algorithm_sort": soi.algorithm_sort,
"modules": modules,
"attachments": attachments,
}
return json.dumps(serialized)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment