Sub-codes system / Sistema de Sub-códigos #1362
Salomon-Mazatlan
started this conversation in
General
Replies: 1 comment
-
|
Amazing, thanks ! |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
English:
Implementation of the sub-codes system in QualCoder, and fixes.
(Note: published for the record)
What the user can do
A sub-code is a code nested inside another code, which makes it possible to build hierarchies of codes and not only of categories. From the interface, the user can:
• Create a sub-code directly from a code's context menu.
• Drag one code onto another to nest it.
• Merge one code into another, keeping the sub-codes of the merged code.
• Search sub-codes from Filter or from the bottom search bar.
(Code tree showing a hierarchy of nested sub-codes)
Counts accumulate in cascade: a parent code's total includes the codings of all its sub-codes, and that total is attributed to the category of the topmost ancestor. This way, a sub-code is never "lost" outside its category's count.
(Counts of a parent code accumulating those of its sub-codes)
Updated files
__main__.pysupercidschema, v16 migration, repairscode_text.pyview_image.pyview_av.pycode_pdf.pyreports.pyrefi.pycode_organiser.pyview_charts.pyreport_codes.pyreport_code_summary.pytext_mining.pycode_color_scheme.py_nest_subcodes_in_treeai_search_dialog.py_nest_subcodes_in_treereport_relations.py_nest_subcodes_in_treereport_exact_matches.py_nest_subcodes_in_treereport_compare_coder_file.py_nest_subcodes_in_treereport_codes_by_segments.py_nest_subcodes_in_treecodebook.py_nest_subcodes_in_treecolor_selector.pymerge_projects.pysupercidview_graph.pyui_dialog_report_code_frequencies(.ui/.py)report_comparison_table.pyreport_cooccurrence.pyDatabase: schema and migration (main.py)
(Schema of the code_name table with the new supercid column)
The core change is the supercid column in the code_name table, which records the parent code of each sub-code.
supercid is nullable: a NULL value means the code is top-level or belongs to a category. A mutual exclusivity also applies between catid and supercid: a code never has both at once, and if for some reason it did, supercid prevails and catid is cleared.
The database version advances to v16. New projects are created with the column already in place; existing projects are migrated automatically when opened.
On every project open, three integrity repairs run:
• Cycle breaking: the category hierarchy (code_cat.supercatid) and the code hierarchy (code_name.supercid) are traversed, and if a cycle is found the link that closes it is cut. This protects against corrupt projects where a branch would disappear.
• Orphaned sub-codes: if the parent code was deleted but supercid still points to it, it is cleared (supercid = NULL).
• Mutual exclusivity: if a code has both catid and supercid at once, catid is cleared.
Coding modules (code_text.py, code_pdf.py, view_image.py, view_av.py)
The functionality was implemented identically across the four modules. Each one includes:
• Tree building with nesting (_make_code_item, iterative placement): a code is top-level only if it has neither catid nor supercid; the rest is nested under its category or its parent code. Placement is iterative, in several passes, because a parent code may itself be a sub-code not yet placed.
• Count roll-up (_effective_catid, _code_total): resolves the topmost ancestor's category and sums the descendants, memoized and cycle-safe.
• Context menu with three new actions: "Add a new sub-code to code", "Expand or collapse branch", and "Merge code into code".
• Drag and drop to nest: dropping one code onto another turns it into a sub-code (mutually exclusive with a category); dropping it on a category or on blank space removes the nesting.
• Cycle prevention (_code_is_descendant, _category_is_descendant): a code cannot be nested under one of its own sub-codes.
• Re-parenting on merge or delete: when merging a code into another, its sub-codes move to the target code without becoming orphans; when deleting a code that has sub-codes, they are re-linked to the deleted code's supercid or, if it was top-level, they adopt its catid.
• Hierarchy-aware filter and search: a code stays visible if it or any of its descendants matches, so a match is never hidden under a parent that does not match.
(Sub-codes in the text coding interface)
Code organiser (code_organiser.py)
Nesting also works on the canvas: links are drawn from the parent code to each sub-code, descendants are collected recursively, and cycles are prevented when nesting. After a merge or a delete, the same integrity repair as on startup is applied (orphans, exclusivity, and cycles). The "Link code under code" action was added.
(Nesting of sub-codes in the code organiser)
Graph view (view_graph.py)
The graph view excludes sub-codes from the top level of categories and adds visual links from each sub-code to its parent code. Each code node loads its supercid.
(Sub-code to parent-code links in the graph view)
Charts (view_charts.py)
Selection now propagates in cascade and includes descendant sub-codes: selecting a category also charts the sub-codes nested under its codes. For hierarchy charts (Treemap and Sunburst), _rollup_subcodes_into_parent_codes was added. Because sub-codes have a null catid, without this roll-up they would float at the root; the method sums each sub-code upward, deepest first, so that the parent's total includes its descendants, showing the parent as the inner segment and the sub-codes as the outer ring. Visual robustness helpers were also added: _contrast_text, which adjusts the legibility of text over the colour, and _apply_code_colours, with defensive guards.
(Sunburst chart with sub-codes)
(Treemap chart with the code hierarchy)
Reports
All report modules became sub-code-aware: reports.py, report_codes.py, report_code_summary.py, report_codes_by_segments.py, report_relations.py, report_exact_matches.py, report_compare_coder_file.py, report_comparison_table.py, and report_cooccurrence.py.
In practice, queries include nested sub-codes, counts accumulate into the category total, and the report's code tree shows the hierarchy. In reports.py, the export distinguishes levels: in both the outline and the tidy data sheet, each level is labelled "Code level 1", "Sub-code level 2", and so on, indented by depth.
REFI-QDA interoperability (refi.py)
Export: recursive XML is emitted, so each code emits its nested sub-codes (via supercid) inside its own element, instead of flattening them.
Import: previously, when the file came from software where a category can also be a code (MAXQDA, NVivo), a category and a mirror code with the same name underneath were created, which flattened the nested hierarchy. Now it is reconstructed correctly:
• A node is a category only if it is explicitly non-codable (isCodable == "false"); everything else is a code.
• A code's codable children are imported as sub-codes (supercid), not as a category plus a mirror code.
• The recursion passes down cat_id when the parent is a category, or super_cid when the parent is a code, and the two are mutually exclusive.
Associated fix: if the name of a category or a code already exists, its existing catid/cid is now reused so that the children still nest. Previously the duplicate was silently ignored (except sqlite3.IntegrityError: pass) and its children became orphans.
(Export to MAXQDA with the sub-code hierarchy)
(Export to ATLAS.ti with the hierarchy preserved)
Propagation to every dialog with a code tree
A reusable helper, _nest_subcodes_in_tree(), was added to codebook.py, ai_search_dialog.py, code_color_scheme.py, report_relations.py, report_exact_matches.py, report_compare_coder_file.py, report_codes_by_segments.py, and reports.py.
The helper runs after fill_tree has placed all the codes and re-parents the sub-code items under their parent code. Instead of rebuilding the item, it moves it, which preserves its flags, checkboxes, colour, and count. It is harmless in projects without sub-codes, because it returns immediately, which guarantees full backward compatibility. It also includes a loop guard (guard < 10000).
Bug fixes and robustness
• Repair of damaged hierarchies on startup (main.py): clearing of orphaned supercid, enforcement of the catid/supercid exclusivity, and cycle breaking, in both codes and categories.
• REFI-QDA import with duplicate names (refi.py): reuse of the existing catid/cid so as not to leave orphaned children, with broader exception handling (sqlite3.Error instead of catching only IntegrityError).
• Defensive guards when reading tree item identifiers (except ValueError) while parsing the cid: and catid: prefixes of the items, replicated across about twelve dialogs. This avoids crashes on items with unexpected text.
• Cycle prevention for categories (sub-categories) in the four coding modules: "Guard against cycles: moving a category under one of its own sub-categories".
• color_selector.py: fix of a typo in the docstring (", ir" became ", or") and a rewrite of the colour filter to a post-order traversal, so that a matching sub-code keeps its parent visible.
• view_charts.py: additional defensive guards in colour application (except (IndexError, AttributeError, TypeError)).
Code frequencies report with breakdown by source
Changes in reports.py and in the ui_dialog_report_code_frequencies dialog (.ui and .py):
• New "Breakdown by source (text / image / A/V)" checkbox, with the tooltip "Show frequencies broken down by where they were coded". The dialog was enlarged (from 75 to 100 in height) to fit it.
• Toggleable per-source column logic (_toggle_source_columns) and a header context menu (_header_menu).
• Multi-sheet Excel export: a report sheet, an outline, and a tidy data sheet. The latter contains only code and sub-code rows (category rows are aggregates) and expresses the hierarchical chain in "Code level 1 / Sub-code level N" columns.
(Code frequencies report with the breakdown by source)
(Report: the indented hierarchy, exactly as displayed.)
(Outline: the same report with native Excel row grouping, so each branch can be collapsed/expanded with the +/- margin buttons)
(Data: a flat 'tidy' sheet (one column per level, plus type and depth), suited to pivot tables, filters and formulas.)
text_mining.py
Support for the sub-codes system and a fix to text positions.
Note: the module is not currently integrated in QualCoder; the change was applied as a precaution.
Compatibility and migration
• Backward: projects without sub-codes work exactly as before, because the nesting helper and the repairs are harmless when there is no supercid.
• Automatic migration: when a v14 or v15 project is opened, the supercid column is added and raised to v16 with no user intervention.
• Regression risk: low, since the new column is nullable.
Español:
Implementación del sistema de sub-códigos en QualCoder y correcciones.
(Nota: publicación para fines de registro)
Lo que puede hacer el usuario
Un sub-código es un código anidado dentro de otro código, lo que permite construir jerarquías de códigos y no solo de categorías. Desde la interfaz, la persona usuaria puede:
• Crear un sub-código directamente desde el menú contextual de un código.
• Arrastrar un código sobre otro para anidarlo.
• Fusionar un código dentro de otro, conservando los sub-códigos del código fusionado.
• Buscar sub-códigos desde Filter o desde la barra de búsqueda inferior.
(Árbol de códigos con una jerarquía de sub-códigos anidados)
Los conteos se acumulan en cascada: el total de un código padre incluye las codificaciones de todos sus sub-códigos, y ese total se atribuye a la categoría del ancestro superior. Así, un sub-código nunca queda “perdido” fuera del recuento de su categoría.
(Conteos de un código padre acumulando los de sus sub-códigos)
Archivos actualizados
__main__.pysupercid, migración v16, reparacionescode_text.pyview_image.pyview_av.pycode_pdf.pyreports.pyrefi.pycode_organiser.pyview_charts.pyreport_codes.pyreport_code_summary.pytext_mining.pycode_color_scheme.py_nest_subcodes_in_treeai_search_dialog.py_nest_subcodes_in_treereport_relations.py_nest_subcodes_in_treereport_exact_matches.py_nest_subcodes_in_treereport_compare_coder_file.py_nest_subcodes_in_treereport_codes_by_segments.py_nest_subcodes_in_treecodebook.py_nest_subcodes_in_treecolor_selector.pymerge_projects.pysupercidview_graph.pyui_dialog_report_code_frequencies(.ui/.py)report_comparison_table.pyreport_cooccurrence.pyBase de datos: esquema y migración (main.py)
(Esquema de la tabla code_name con la nueva columna supercid)
El cambio de fondo es la columna supercid en la tabla code_name, que registra el código padre de cada sub-código.
supercid es anulable: un valor NULL indica que el código es de nivel superior o que pertenece a una categoría. Rige además una exclusividad mutua entre catid y supercid: un código nunca tiene ambos a la vez, y si por algún motivo los tuviera, supercid prevalece y catid se anula.
La versión de la base de datos avanza a v16. Los proyectos nuevos se crean ya con la columna; los proyectos existentes se migran de forma automática al abrirlos.
En cada apertura de proyecto se ejecutan tres reparaciones de integridad:
• Sub-códigos huérfanos: si el código padre fue eliminado pero supercid quedó apuntando a él, se limpia (supercid = NULL).
• Exclusividad mutua: si un código tiene catid y supercid al mismo tiempo, se anula catid.
• Ruptura de ciclos: se recorre la jerarquía de categorías (code_cat.supercatid) y de códigos (code_name.supercid), y si se detecta un ciclo se corta el enlace que lo cierra. Esto protege contra proyectos corruptos en los que una rama desaparecería.
Módulos de codificación (code_text.py, code_pdf.py, view_image.py, view_av.py)
La funcionalidad se implementó de forma idéntica en los cuatro módulos. Cada uno incorpora:
• Construcción del árbol con anidamiento (_make_code_item, colocación iterativa): un código es de nivel superior solo si no tiene catid ni supercid; el resto se anida bajo su categoría o su código padre. La colocación es iterativa, en varias pasadas, porque un código padre puede ser a su vez un sub-código aún no colocado.
• Acumulación de conteos (_effective_catid, _code_total): resuelve la categoría del ancestro superior y suma los descendientes, de forma memoizada y a prueba de ciclos.
• Menú contextual con tres acciones nuevas: “Add a new sub-code to code”, “Expand or collapse branch” y “Merge code into code”.
• Arrastrar y soltar para anidar: soltar un código sobre otro lo convierte en sub-código (excluyente con categoría); soltarlo en una categoría o en blanco elimina el anidamiento.
• Prevención de ciclos (_code_is_descendant, _category_is_descendant): no se puede anidar un código bajo uno de sus propios sub-códigos.
• Re-parentado al fusionar o eliminar: al fusionar un código en otro, sus sub-códigos pasan al código destino sin quedar huérfanos; al eliminar un código con sub-códigos, estos se re-enlazan al supercid del eliminado o, si era de nivel superior, adoptan su catid.
• Filtro y búsqueda conscientes de la jerarquía: un código permanece visible si él o cualquiera de sus descendientes coincide, de modo que una coincidencia nunca queda oculta bajo un padre que no coincide.
(Sub-códigos en la interfaz de codificación de texto)
Organizador de códigos (code_organiser.py)
El anidamiento también funciona en el lienzo: se dibujan enlaces del código padre a cada sub-código, se recolectan los descendientes de forma recursiva y se previenen los ciclos al anidar. Tras una fusión o un borrado se aplica la misma reparación de integridad que en el arranque (huérfanos, exclusividad y ciclos). Se añadió la acción “Link code under code”.
(Anidamiento de sub-códigos en el organizador de códigos)
Vista de grafo (view_graph.py)
La vista de grafo excluye los sub-códigos del nivel superior de categorías y añade enlaces visuales desde cada sub-código hacia su código padre. Cada nodo de código carga su supercid.
(Enlaces de sub-código a código padre en la vista de grafo)
Gráficas (view_charts.py)
La selección se propaga ahora en cascada e incluye los sub-códigos descendientes: seleccionar una categoría grafica también los sub-códigos anidados bajo sus códigos. Para los gráficos de jerarquía (Treemap y Sunburst) se añadió _rollup_subcodes_into_parent_codes. Como los sub-códigos tienen catid nulo, sin este roll-up flotarían en la raíz; el método suma cada sub-código hacia arriba, de los más profundos primero, para que el total del padre incluya a sus descendientes, mostrando el padre como segmento interno y los sub-códigos como anillo externo. Se incorporaron además funciones de robustez visual: _contrast_text, que ajusta la legibilidad del texto sobre el color, y _apply_code_colours, con guardas defensivas.
(Gráfico Sunburst con sub-códigos)
(Gráfico Treemap con la jerarquía de códigos)
Reportes
Todos los módulos de reporte se volvieron conscientes de los sub-códigos: reports.py, report_codes.py, report_code_summary.py, report_codes_by_segments.py, report_relations.py, report_exact_matches.py, report_compare_coder_file.py, report_comparison_table.py y report_cooccurrence.py.
En la práctica, las consultas incluyen los sub-códigos anidados, los conteos se acumulan en el total de la categoría y el árbol de códigos del reporte muestra la jerarquía. En reports.py, la exportación distingue niveles: tanto en el esquema (outline) como en la hoja de datos ordenada (tidy), cada nivel se etiqueta como “Code level 1”, “Sub-code level 2”, y así sucesivamente, con sangría según la profundidad.
Interoperabilidad REFI-QDA (refi.py)
Exportación: se emite XML recursivo, de modo que cada código emite sus sub-códigos anidados (vía supercid) dentro de su propio elemento, en lugar de aplanarlos.
Importación: antes, cuando el archivo provenía de software donde una categoría también puede ser código (MAXQDA, NVivo), se creaba una categoría y un código espejo con el mismo nombre debajo, lo que aplanaba la jerarquía anidada. Ahora se reconstruye correctamente:
• Un nodo es categoría solo si es explícitamente no codificable (isCodable == "false"); todo lo demás es código.
• Los hijos codificables de un código se importan como sub-códigos (supercid), no como categoría más código espejo.
• La recursión propaga cat_id cuando el padre es una categoría, o super_cid cuando el padre es un código, y ambos son mutuamente excluyentes.
Corrección asociada: si el nombre de una categoría o de un código ya existe, ahora se reutiliza su catid/cid para que los hijos sigan anidando. Antes el duplicado se ignoraba en silencio (except sqlite3.IntegrityError: pass) y sus hijos quedaban huérfanos.
(Exportación a MaxQDA con la jerarquía de sub-códigos)
(Exportación a ATLAS.ti con la jerarquía preservada)
Propagación a todos los diálogos con árbol de códigos
Se añadió un ayudante reutilizable, _nest_subcodes_in_tree(), a codebook.py, ai_search_dialog.py, code_color_scheme.py, report_relations.py, report_exact_matches.py, report_compare_coder_file.py, report_codes_by_segments.py y reports.py.
El ayudante se ejecuta después de que fill_tree colocó todos los códigos y re-parenta los ítems de sub-código bajo su código padre. En lugar de reconstruir el ítem, lo mueve, lo que preserva sus banderas, casillas de verificación, color y conteo. Es inocuo en proyectos sin sub-códigos, porque retorna de inmediato, lo que garantiza compatibilidad total hacia atrás. Incluye además un guard contra bucles (guard < 10000).
Correcciones de errores y robustez
• Reparación de jerarquías dañadas en el arranque (main.py): limpieza de supercid huérfanos, aplicación de la exclusividad catid/supercid y ruptura de ciclos, tanto en códigos como en categorías.
• Importación REFI-QDA con nombres duplicados (refi.py): reutilización del catid/cid existente para no dejar hijos huérfanos, con un manejo de excepciones más amplio (sqlite3.Error en lugar de capturar solo IntegrityError).
• Guardas defensivas al leer identificadores del árbol (except ValueError) al interpretar los prefijos cid: y catid: de los ítems, replicadas en alrededor de doce diálogos. Esto evita caídas ante ítems con texto inesperado.
• Prevención de ciclos en categorías (sub-categorías) en los cuatro módulos de codificación: “Guard against cycles: moving a category under one of its own sub-categories”.
• color_selector.py: corrección de un error tipográfico en el docstring (“, ir” pasó a “, or”) y reescritura del filtro por color a un recorrido en post-orden, de modo que un sub-código que coincide mantiene visible a su padre.
• view_charts.py: guardas defensivas adicionales en la aplicación de colores (except (IndexError, AttributeError, TypeError)).
Reporte de frecuencias con desglose por fuente
Cambios en reports.py y en el diálogo ui_dialog_report_code_frequencies (.ui y .py):
• Nueva casilla “Breakdown by source (text / image / A/V)”, con el tooltip “Show frequencies broken down by where they were coded”. El diálogo se agrandó (de 75 a 100 de alto) para alojarla.
• Lógica de columnas por fuente conmutables (_toggle_source_columns) y menú contextual en la cabecera (_header_menu).
• Exportación a Excel en varias hojas: hoja de reporte, esquema (outline) y hoja de datos ordenada (tidy). Esta última contiene solo filas de código y sub-código (las de categoría son agregados) y expresa la cadena jerárquica en columnas “Code level 1 / Sub-code level N”.
(Reporte de frecuencias con el desglose por fuente)
(Report: the indented hierarchy, exactly as displayed.)
(Outline: the same report with native Excel row grouping, so each branch can be collapsed/expanded with the +/- margin buttons)
(Data: a flat 'tidy' sheet (one column per level, plus type and depth), suited to pivot tables, filters and formulas.)
text_mining.py
Soporte para el sistema de sub-códigos y corrección de las posiciones de texto.
Nota: el módulo no está integrado actualmente en QualCoder; el cambio se aplicó de forma preventiva.
Compatibilidad y migración
• Hacia atrás: los proyectos sin sub-códigos funcionan igual que antes, porque el ayudante de anidamiento y las reparaciones son inocuos cuando no hay supercid.
• Migración automática: al abrir un proyecto v14 o v15, se añade la columna supercid y se eleva a v16 sin intervención de la persona usuaria.
• Riesgo de regresión: bajo, ya que la columna nueva es anulable.
Beta Was this translation helpful? Give feedback.
All reactions