Package: data-wizard-edx-copy
Django Data Wizardis an interactive tool for mapping tabular data (e.g. Excel, CSV, XML, JSON) into a normalized database structure viaDjango REST FrameworkandIterTable.  Django Data Wizard allows novice users to map spreadsheet columns to serializer fields (and cell values to foreign keys) on-the-fly during the import process.  This reduces the need for preset spreadsheet formats, which most data import solutions require.The Data Wizard supports straightforward one-to-one mappings from spreadsheet columns to database fields, as well as more complex scenarios likenatural keysandEntity-Attribute-Value(or "wide") table mappings.  It was originally developed for use with theERAV data modelprovided byvera.UsageDjango Data Wizard provides aweb interface,JSON API, andCLIfor specifying adata sourceto import (e.g. a previously-uploaded file), selecting aserializer, mapping the datacolumnsandidentifiers, and (asynchronously) importing thedatainto any target model in the database.Data Wizard is designed to allow users to iteratively refine their data import flow.  For example, decisions made during an initial data import are preserved for future imports of files with the same structure.  The includeddata modelmakes this workflow possible.Table Of ContentsGetting StartedInstallationInitial ConfigurationTarget Model Registration (required)API DocumentationRun API & Admin ScreensData ModelCommand-Line InterfaceAdvanced CustomizationCustom SerializersCustom Data SourcesCustom TasksTask Backendswq Framework IntegrationInstallation# Recommended: create virtual environment# python3 -m venv venv# . venv/bin/activatepython3-mpipinstalldata-wizardSeehttps://github.com/wq/django-data-wizardto report any issues.Initial ConfigurationWithin a new or existing Django project, adddata_wizardto yourINSTALLED_APPS:# myproject/settings.pyINSTALLED_APPS=(# ...'data_wizard','data_wizard.sources',# Recommended)# This can be omitted to use the defaultsDATA_WIZARD={'BACKEND':'data_wizard.backends.threading','LOADER':'data_wizard.loaders.FileLoader','IDMAP':'data_wizard.idmap.existing','AUTHENTICATION':'rest_framework.authentication.SessionAuthentication','PERMISSION':'rest_framework.permissions.IsAdminUser','AUTO_IMPORT_TASKS':('data_wizard.tasks.check_serializer','data_wizard.tasks.check_iter','data_wizard.tasks.check_columns','data_wizard.tasks.check_row_identifiers','data_wizard.tasks.import_data',),}If you would like to use the built-in data source tables (FileSourceandURLSource), also includedata_wizard.sourcesin yourINSTALLED_APPS.  Otherwise, you will want to configure one or morecustom data sources (see below).Note: By default, Django Data Wizard uses a simplethreading backendfor executing asynchronous tasks.Celery and Rediscan be used instead, but this is not required.Next, add"data_wizard.urls"to your URL configuration.# myproject/urls.pyfromdjango.urlsimportpath,includeurlpatterns=[# ...path('datawizard/',include('data_wizard.urls')),]Target Model RegistrationIn order to use the wizard, youmustregister one or more target models and/or serializers.  Target model registration helps the wizard know where to put the data it finds in each row of the source spreadsheet.  (By contrast,sourcemodel registration is optional, as long as you are using the provideddata_wizard.sourcesapp.)The registration API is modeled after the  Django admin andadmin.py.  Specifically, Data Wizard will look for awizard.pyfile in your app directory, which should have the following structure:# myapp/wizard.pyimportdata_wizardfrom.modelsimportMyModeldata_wizard.register(MyModel)Internally, the wizard will automatically create a Django REST Framework serializer class corresponding to the target model.  If needed, you can also specify acustom serializer classto configure how the target model is validated and populated.Once everything is configured, upload a source file in the Django admin, select "Import via data wizard" from the admin actions menu, and navigate through the screens described below.API DocumentationDjango Data Wizard is implemented as a series of views that can be accessed via the Django admin as well as via a JSON API.New RunPOST /datawizard/Creates a new instance of the wizard (i.e. aRun).  If you are using the Django admin integration, this step is executed when you select "Import via Data Wizard" from the admin actions menu.  If you are using the JSON API, the returned runidshould be used in all subsequent calls to the API.  EachRunis tied to the source model via ageneric foreign key.parameterdescriptionobject_idThe primary key of thesourcemodel instance containing the data to be imported.content_type_idThesourcemodel's app label and model name (in the formatapp_label.modelname).loader(Optional) The class name to use for loading the source dataset viaIterTable.  The default loader (data_wizard.loaders.FileLoader) assumes that the source model contains aFileFieldnamedfile.serializer(Optional) The serializer class to use when populating thetargetmodel.  This can be left unset to allow the user to select the target during the wizard run.autoPOST /datawizard/[id]/autoTheautotask attempts to run the entire data wizard process from beginning to end.  If any input is needed, the import will halt and redirect to the necessary screen.  If no input is needed, theautotask is equivalent to starting thedatatask directly.  This is an asynchronous method, and returns atask_idto be used with the status API.Therun_detail.htmltemplate provides an example form that initiates theautotask.  Theautotask itself uses therun_auto.htmltemplate.The default sequence of tasks is defined by theAUTO_IMPORT_TASKSsettingNote that thecheck_*tasks do not provide a direct UI or HTTP API.  Instead, these tasks redirect to a corresponding UI task by raisingdata_wizard.InputNeededif necessary.  For example,data_wizard.tasks.check_columnsraisesInputNeededand redirects to thecolumnstask if the spreadsheet contains unexpected column headers.  Once the form is submitted, theupdatecolumnstask processes the user input and runs the check again.  Once the check succeeds (i.e. all columns have been mapped), the user is able to restart the auto task.Here are the corresponding Input and Form Processing tasks for each of the tasks in the default sequence:Auto TaskInput TaskForm Processing Taskcheck_serializerlist_serializersupdateserializercheck_iterN/AN/Acheck_columnsread_columnsupdate_columnscheck_row_identifiersread_row_identifiersupdate_row_identifiersimport_dataN/AN/ASeeCustom Tasksfor details on customizing the task sequence.Source:data_wizard.tasks.auto_importstatusGET /datawizard/[id]/status.json?task=[task]ThestatusAPI is used to check the status of an asynchronous task (one ofautoordata).  The API is used by the provideddata_wizard/js/progress.jsto update the<progress>bar in therun_auto.htmlandrun_data.htmltemplates.  Unlike the other methods, this API is JSON-only and has no HTML equivalent.  An object of the following format will be returned:{// General properties"status":"PROGRESS",// or "SUCCESS", "FAILURE""stage":"meta",// or "data""current":23,// currently processing row"total":100,// total number of rows// "FAILURE""error":"Error Message",// Task complete ("SUCCESS")"action":"records",// or "serializers", "columns" "ids""message":"Input Needed",// if action is not "records""skipped":[...],// rows that could not be imported"location":"/datawizard/[id]/records",}The potential values for thestatusfield are the same as commonCelery task states, even when not using thecelerybackend.  When running anautotask, the result isSUCCESSwhenever the task ends without errors, even if there is additional input needed to fully complete the run.The defaultrun_auto.htmlandrun_data.htmltemplates include a<progress>element for use with the status task.serializersGET /datawizard/[id]/serializersTheserializerstask provides a list of all registered serializers (i.e. target models).  This screen is shown by theautotask if a serializer was not specified when theRunwas created.  The defaultrun_serializers.htmltemplate includes an interface for selecting a target.  If a serializer is already selected, the template will display the label and a button to (re)start theautotask.Source:data_wizard.tasks.list_serializersupdateserializerPOST /datawizard/[id]/updateserializerTheupdateserializertask updates the specifiedRunwith the selected target serializer name.  This is typically called fromthe formgenerated by theserializerstask, and will redirect to that task when complete.parameterdescriptionserializerThe class name (or label) of the target serializer to use for this run.Source:data_wizard.tasks.updateserializercolumnsGET /datawizard/[id]/columnsThecolumnstask lists all of the columns found in the source dataset (i.e. spreadsheet) and their mappings to target serializer fields.  This screen is shown by theautotask if there are any column names that could not be automatically mapped.  The potential mappings are one of:simple serializer field names (e.g.field)nested field names (fornatural keys, e.g.nested[record][field])EAVattribute-value mappings (e.g.values[][value];attribute_id=1).  Note that EAV support requires acustom serializer class.The defaultrun_columns.htmltemplate includes an interface for mapping data columns to serializer fields.  If all columns are already mapped, the template will display the mappings and a button to (re)start theautotask.Source:data_wizard.tasks.read_columnsupdatecolumnsPOST /datawizard/[id]/updatecolumnsTheupdatecolumnstask saves the specified mappings from source data columns to target serializer fields.  This is typically called fromthe formgenerated by thecolumnstask, and will redirect to that task when complete.parameterdescriptionrel_[relid]The column to map to the specified serializer field.  Therelidand the complete list of possible mappings will be provided by thecolumnstask.Source:data_wizard.tasks.update_columnsidsGET /datawizard/[id]/idsTheidstask lists all of the foreign key values found in the source dataset (i.e. spreadsheet).  If there are any unmapped foreign key values, the auto task will stop and redirect to theidstask.  The defaultrun_ids.htmltemplate includes an interface for mapping row identifiers to foreign key values.  The potential mappings depend on the serializer field used to represent the foreign key.ForPrimaryKeyRelatedField,SlugRelatedField, andNaturalKeySerializer, the choices will include all existing record ID or slugs.ForNaturalKeySerializeronly, a"new"choice will also be included, allowing for the possibility of creating new records in the foreign table on the fly.Once all ids are mapped, the template will display the mappings and a button to (re)start theautotask.Note that theautotask will skip theidstask entirely if any of the following are true:The file contains no foreign key columnsAll foreign key values were already mapped during a previous import runAll foreign key values can be automatically mapped via theDATA_WIZARD['IDMAP']setting:DATA_WIZARD['IDMAP']detail"data_wizard.idmap.existing"(default)Automatically map existing IDs, but require user to map unknown ids"data_wizard.idmap.never"Require user to manually map all IDs the first time they are found in a file"data_wizard.idmap.always"Always map IDs (skip manual mapping).  Unknown IDs will be passed on as-is to the serializer, which will cause per-row errors unless using natural keys.(custom import path)The function should accept an identifier and a serializer field, and return the mapped value (orNoneif no automatic mapping is available).  See thebuilt-in functionsfor examples.Note that the configuredIDMAPfunction will only be called the first time a new identifier is encountered.  Once the mapping is established (manually or automatically), it will be re-used in subsequent wizard runs.Source:data_wizard.tasks.read_row_identifiersupdateidsPOST /datawizard/[id]/updateidsTheupdateidstask saves the specified mappings from row identifiers to foreign key values.  This is typically called fromthe formgenerated by theidstask, and will redirect to that task when complete.parameterdescriptionident_[identid]_idThe identifier to map to the specified foreign key value.  Theidentidand the complete list of possible mappings will be provided by theidstask.Source:data_wizard.tasks.update_row_identifiersdataPOST /datawizard/[id]/dataThedatatask starts the actual import process (and is called byautobehind the scenes).  Unlikeauto, callingdatadirectly will not cause a redirect to one of the other tasks if any meta input is needed.  Instead,datawill attempt to import each record as-is, and report any errors that occured due to e.g. missing fields or unmapped foreign keys.This is an asynchronous method, and returns atask_idto be used with thestatusAPI.  The defaultrun_data.htmltemplate includes a<progress>element for use with status task.Source:data_wizard.tasks.import_datarecordsGET /datawizard/[id]/recordsTherecordstask provides a list of imported rows (including errors).  It is redirected to by theautoanddatatasks upon completion.  Successfully importedRecordinstances will have ageneric foreign keypointing to the target model.  Therecordstask will include links to theget_absolute_url()or admin screen for each newly imported target model instance.  The defaultrun_records.htmltemplate includes an interface for displaying the record details.Run ListGET /datawizard/Django Data Wizard provides a list view that summarises prior runs and the number of records imported by each.  Incomplete runs can also be restarted from this list.Identifier AdminGET /admin/data_wizard/identifer/Django Data Wizard identifier mappings can be viewed and edited via the Django Admin.  Runs can also be viewed through the admin - though the Run List above will generally be more useful.Data ModelDjango Data Wizard provides a number of Django models that help track the import process, and preserve data mapping decisions for future reuse.  While asourcemodel is required, yourtargetdata model(s) generally do not have to be changed to support Data Wizard integration.stepdescriptionmodel0UploadsourcefileCreateFileSource(or custom source model)1Start data wizard runCreateRun2Select serializer (& target model)UpdateRun3Map columns to database field namesOneIdentifierper column, if needed4Map cell values to foreign keysOneIdentifierper unique value5Import data intotargetmodelOneRecord+ one target model instance per rowTheRunmodel includes ageneric foreign keypointing to the source model (e.g.FileSource.)  Each row in the source spreadsheet will be mapped to aRecord.  If the row was successfully imported, a new instance of the target data model will be created, and theRecordwill have a generic foreign key pointing to it.  TheIdentifiermodel contains no foreign keys, since identifier mappings are reused for subsequent imports.  Instead, a separateRangemodel tracks the location(s) (rows/columns) of eachIdentifierin eachRun.Note that the above workflow just describes the most common use case.  You can createcustom serializersthat update more than one target data model per spreadsheet row.  And you can specifycustom data sourcesthat might not be a spreadsheet or even a file.Command-Line InterfaceDjango Data Wizard provides a singlemanagement command,runwizard, that can be used to initiate theautotask from the command line.  This can be used to facilitate automated processing, for example as part of a regular cron job.  Note that the CLI does not (currently) support interactively mapping columns and ids, so these should be pre-mapped using the web or JSON API.Usage:./manage.pyrunwizardmyapp.mymodel123\--loadermyapp.loaders.customloader\--serializermyapp.serializer.customserializer\--usernamemyusernameThe basic usage is similar to theNew Run API.  Only a content type and object id are required, while the other arguments will be auto-detected if possible.  In particular, you may want to useset_loader()to predefine the defaultloaderandserializerfor any models you plan to use with the CLI.Therunwizardcommand will create a newRunand immediately start theautotask.  Errors will be shown on the console.Custom SerializersData Wizard uses instances of Django REST Framework'sSerializer classto determine the destination fields on the target model.  Specifically, the default serializer isNaturalKeyModelSerializer, which is based onModelSerializer.You can override the default serializer by callingdata_wizard.register()with a name and a serializer class instead of a model class.  Multiple serializers can be registered with the wizard to support multiple import configurations and target models.# myapp/wizard.pyfromrest_frameworkimportserializersimportdata_wizardfrom.modelsimportTimeSeriesclassTimeSeriesSerializer(serializers.ModelSerializer):# (custom fields here)classMeta:model=TimeSeriesfields='__all__'# Optional - see options belowdata_wizard={'header_row':0,'start_row':1,'show_in_list':True,'idmap':data_wizard.idmap.existing,'auto_import_tasks':[customtasklist],}# Use default name & serializerdata_wizard.register(TimeSeries)# Use custom name & serializerdata_wizard.register("Time Series - Custom Serializer",TimeSeriesSerializer)At least one serializer or model should be registered in order to use the wizard.  Note the use of a human-friendly serializer label when registering a serializer.  This name should be unique throughout the project, but can be changed later on without breaking existing data.  (The class path is used as the actual identifier behind the scenes.)Serializer OptionsIn general, custom serializers have all the capabilities of regularDjango REST Framework serializers, including custom validation rules and the ability to populate multiple target models.  While therequestcontext is not available, information about the run (including the user) can be retrieved through thedata_wizardcontext instead.When overriding a serializer for anatural key model, be sure to extendNaturalKeyModelSerializer, as inthis example.  In other cases, extendModelSerializer(as in the example above) or the baseSerializerclass.Custom serializers can be used to supportEntity-Attribute-Valuespreadsheets where the attribute names are specified as additional columns.  To support this scenario, theEntityserializer should include a nestedValueserializer withmany=True, and theValueserializer should have a foreign key to theAttributetable, as inthis example.Data Wizard also supports additional configuration by setting adata_wizardattribute on theMetaclass of the serializer.  The following options are supported.namedefaultnotesheader_row0Specifies the first row of the spreadsheet that contains column headers.  If this is greater than 0, the space above the column headers will be scanned for anything that looks like a one-off "global" value intended to be applied to every row in the imported data.start_row1The first row of data.  If this is greater thanheader_row + 1, the column headers will be assumed to span multiple rows.  A common case is when EAV parameter names are on the first row and units are on the second.show_in_listTrueIf set toFalse, the serializer will be available through the API but not listed in the wizard views.  This is useful if you have a serializer that should only be used during fully automated workflows.idmapIDMAPsettingCan be any ofdata_wizard.idmap.*or a custom function.  Unlike theIDMAPsetting, this should always be an actual function and not an import path.auto_import_tasksAUTO_IMPORT_TASKSsettingA list of import paths to functions registered with@data_wizard.wizard_task(seeCustom Tasks.Custom Data SourcesDjango Data Wizard usesIterTableto determine the source columns present on the spreadsheet or other data source.  Django Data Wizard can use any Django model instance as a source for its data, provided there is a registered loader that can convert the source model into aIter class.  Data Wizard provides two out-of-the the box loaders,FileLoaderandURLLoader, that can be used with the provided models indata_wizard.sources(FileSourceandURLSource, respectively).Extending FileLoaderThe defaultFileLoadercan be used with any Django model with aFileFieldnamedfile.  You can use a model with a differentFileFieldname by creating a subclass ofdata_wizard.loaders.FileLoaderand setting it as the loader for your model.# myapp/models.pyfromdjango.dbimportmodelsclassFileModel(models.Model):spreadsheet=models.FileField(upload_to='spreadsheets')# myapp/loaders.pyfromdata_wizardimportloadersclassFileLoader(loaders.FileLoader):file_attr='spreadsheet'# myapp/wizard.pyimportdata_wizardfrom.modelsimportFileModeldata_wizard.set_loader(FileModel,"myapp.loaders.FileLoader")If you have a generic loader that can work with multiple source models, you can also set the default loader globally:# myapp/settings.pyDATA_WIZARD={'LOADER':'myapp.loaders.FileLoader'}You should register a customModelAdminclass to add the Import action in the admin panel for your model.# myapp/admin.pyfromdjango.contribimportadminfromdata_wizard.adminimportImportActionModelAdminfrom.modelsimportFileModel@admin.register(FileModel)classFileModelAdmin(ImportActionModelAdmin):passCustom LoaderThe default loaders support any file format supported byIterTable(Excel, CSV, JSON, and XML).  Additional formats can be integrating by creating acustom IterTable classand then registering it with the wizard.  For example, theClimata Vieweruses Django Data Wizard to import data fromclimata's IterTable-based web service client.  To do this, extenddata_wizard.loaders.BaseLoaderwith a customload_iter()function that returns the data from IterTable, as in the example below.It is likely that you will want to use a specific serializer with your custom loader.  If so, overridedefault_serializerorget_serializer_name()on the loader.  By default, these returnNone, which requires the user to specify the serializer when creating or executing theRun.# myapp/models.pyfromdjango.dbimportmodelsclassCustomIterSource(models.Model):some_option=models.TextField()# myapp/loaders.pyfromdata_wizardimportloadersfrom.iterimportCustomIterclassCustomIterLoader(loaders.BaseLoader):default_serializer='mydataapp.wizard.CustomSerializer'defload_iter(self):source=self.run.content_objectreturnCustomIter(some_option=source.some_option)# myapp/wizard.pyimportdata_wizardfrom.modelsimportCustomIterSourcedata_wizard.set_loader(CustomIterSource,"myapp.loaders.CustomIterLoader")Custom TasksIt is possible to override the sequence of tasks excecuted by theautotask, to remove steps or add new ones.  The list can be overridden globally (by settingDATA_WIZARD['AUTO_IMPORT_TASKS']), or on a per-serializer basis (by settingMeta.data_wizard['auto_import_tasks']).Each custom task function should be registered with the@data_wizard.wizard_taskdecorator to configure a label and optionally the API path relative to/datawizard/[id]/.  The arguments to the decorator and the function determine the task type.Check Tasks@data_wizard.wizard_task(label="Custom Check",url_path=False)defcustom_check(run):ifnotsome_condition_satisfied(run):raisedata_wizard.InputNeeded("custominput")Check tasks validate whether some condition is satisfied, redirecting to an Input task if needed.url_pathis usually set to False to disable the HTTP endpoint.  The task label will be shown in the progress bar (if the task takes more than a couple seconds to run).Input Tasks@data_wizard.wizard_task(label="Custom Input",url_path="custominput")defcustom_input(run):return{"some_template_context":[]}Input tasks enable the user to provide feedback to guide the wizard.  They should have aurl_path(which will default to the function name) and a corresponding template (e.g.data_wizard/run_custominput.html).  The context returned by the task will be in the template under theresultvariable.  The template typically either renders a form with the needed inputs, or (if all inputs have been entered) a summary with the option to restart the auto task.Form Processing Tasks@data_wizard.wizard_task(label="Custom Input",url_path="customsave")defcustom_save(run,post={}):some_save_method(run,post)return{**custom_input(run),"current_mode":"custominput",}Form Processing Tasks process the form submitted from a prior input task.  Registration is similar to Input Tasks except the function itself should accept an optionalpostkwarg.  Form Processing tasks should be registered withurl_path, and redirect back to the input task (by settingcurrent_modeon the response).Custom Auto TasksIn very advanced use cases, it might be necessary to generate the list of tasks dynamically depending on a number of factors.  In that case, it is possible to define a fully custom auto task:@data_wizard.wizard_task(label="Custom Workflow",url_path="customauto",use_async=True)defcustom_auto(run):task_list=somehow_determine_tasks(run)returnrun.run_all(task_list)Registration is similar as that for other tasks with the addition of theuse_asynckeyword, which facilitates background processing via the configured task backend.In general, the tasks in an automated task list should be check tasks or other auto tasks.  Input and Form Processing tasks should be executed outside of the automated flow.Task BackendsAny of the following backends can be configured with via theBACKENDsetting:# myproject/settings.pyDATA_WIZARD={"BACKEND":"data_wizard.backends.threading"# Default"data_wizard.backends.immediate""data_wizard.backends.celery"}data_wizard.backends.threadingThethreadingbackend creates a separate thread for long-running asynchronous tasks (i.e.autoanddata).  The threading backend leverages the Django cache to pass results back to the status API.  This is the default backend, but it is still a good idea to set it explicitly.data_wizard.backends.immediateTheimmediatebackend completes all processing before returning a result to the client, even for the otherwise "asynchronous" tasks (autoanddata).  This backend is suitable for small spreadsheets, or for working around threading issues.  This backend maintains minimal state, and is not recommended for use cases involving large spreadsheets or multiple simultanous import processes.data_wizard.backends.celeryThecelerybackend leveragesCeleryto handle asynchronous tasks, and is usually used withRedisas the memory store.
These additional dependencies are not installed with Django Data Wizard by default.  If you want to use this backend, be sure to configure these libraries first or the REST API may not work as expected.  You can use these steps on Ubuntu:# Install redis and celerysudoapt-getinstallredis-server
pipinstallceleryredisOnce Redis is installed, configure the following files in your project:# myproject/settings.pyDATA_WIZARD{'BACKEND':'data_wizard.backends.celery'}CELERY_RESULT_BACKEND=BROKER_URL='redis://localhost:6379/1'# myproject/celery.pyfrom__future__importabsolute_importfromceleryimportCeleryfromdjango.confimportsettingsapp=Celery('myproject')app.config_from_object('django.conf:settings')app.autodiscover_tasks(lambda:settings.INSTALLED_APPS)# myproject/__init__.pyfrom.celeryimportappascelery_appFinally, run celery withcelery -A myproject.  You may want to use celery'sdaemonizationto keep the process running in the background.  Any time you change your serializer registration, be sure to reload celery in addition to restarting the Django WSGI instance.Note that the requirement for an extra daemon means this backend can break more easily after a server restart.  Even worse, you may not notice that the backend is down for several months (e.g. until a user tries to import a spreadsheet).  For this reason,we recommend using one of the other backendsunless you are already using celery for other background processing tasks.wq Framework integrationThe Django Data Wizard has built-in support for integration with thewq framework.  On the server, configuration is mostly the same, except that you do not need to add"data_wizard.urls"to your urls.py as the wizard will register itself withwq.dbinstead.Data Wizard provides mustache templates for each of the above tasks to integrate with the wq.app UI.  These are rendered on the server and do not need to be included in your JavaScript build.  However, you should install the@wq/progressplugin via NPM and register it with@wq/app.// src/index.jsimportappfrom'@wq/app';importprogressfrom'@wq/progress';app.use(progress);app.init(config).then(...);Once everything is set up, add the following<form>to the detail template that wq generates for your source model.  Note that you will need to add this<form>manually even if the source model is one ofdata_wizard.sources.  After adding the form, be sure to skip template regeneration for the source model.<!-- filesource_detail.html --><h1>{{label}}</h1><ahref="{{rt}}/media/{{file}}"rel="external">Download File</a><formaction="{{rt}}/datawizard/"method="post">{{>csrf}}<inputtype="hidden"name="content_type_id"value="sources.filesource"><inputtype="hidden"name="object_id"value="{{id}}"><buttontype="submit">Import Data from This File</button></form>