diff --git a/web/app/__init__.py b/web/app/__init__.py
index b25e7cd43552bb695f5209ef7523ee6f18e4a77c..7592345e4015a98540dc3fc65326844019e7d97a 100644
--- a/web/app/__init__.py
+++ b/web/app/__init__.py
@@ -55,4 +55,7 @@ def create_app(config_name):
     from .services import services as services_blueprint
     app.register_blueprint(services_blueprint, url_prefix='/services')
 
+    from .results import results as results_blueprint
+    app.register_blueprint(results_blueprint, url_prefix='/results')
+
     return app
diff --git a/web/app/admin/tables.py b/web/app/admin/tables.py
index 4045227893220127bde912b90b0ccbd522d89f62..521967846118cb2bea4bfdf0d0ccb2cd82803993 100644
--- a/web/app/admin/tables.py
+++ b/web/app/admin/tables.py
@@ -2,9 +2,9 @@ from flask_table import Table, Col, LinkCol
 
 
 class AdminUserTable(Table):
-    """
+    '''
     Declares the table describing colum by column.
-    """
+    '''
     classes = ['highlight', 'responsive-table']
     username = Col('Username', column_html_attrs={'class': 'username'},
                    th_html_attrs={'class': 'sort',
@@ -28,9 +28,9 @@ class AdminUserTable(Table):
 
 
 class AdminUserItem(object):
-    """
+    '''
     Describes one item like one row per table.
-    """
+    '''
 
     def __init__(self, username, email, role_id, confirmed, id):
         self.username = username
diff --git a/web/app/models.py b/web/app/models.py
index 43f9e90d73202e5f8e190e7c2651f3e4b196e929..5d0b7ff5764c83829f2639c767decfbbd14b5338 100644
--- a/web/app/models.py
+++ b/web/app/models.py
@@ -129,6 +129,8 @@ class User(UserMixin, db.Model):
                               cascade='save-update, merge, delete')
     jobs = db.relationship('Job', backref='creator', lazy='dynamic',
                            cascade='save-update, merge, delete')
+    results = db.relationship('Result', backref='creator', lazy='dynamic',
+                              cascade='save-update, merge, delete')
 
     def to_dict(self):
         return {'id': self.id,
@@ -532,6 +534,34 @@ class Corpus(db.Model):
         return '<Corpus {corpus_title}>'.format(corpus_title=self.title)
 
 
+class Result (db.Model):
+    '''
+    Class to define a result set of one query.
+    '''
+    __tablename__ = 'results'
+    id = db.Column(db.Integer, primary_key=True)
+    # Foreign keys
+    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
+    # Relationships'
+    corpus_metadata = db.Column(db.JSON())
+    file = db.relationship('ResultFile', backref='result', lazy='dynamic',
+                           cascade='save-update, merge, delete')
+
+
+class ResultFile(db.Model):
+    '''
+    Class to define a ResultFile
+    '''
+    __tablename__ = 'result_files'
+    # Primary key
+    id = db.Column(db.Integer, primary_key=True)
+    # Foreign keys
+    result_id = db.Column(db.Integer, db.ForeignKey('results.id'))
+    # Fields
+    filename = db.Column(db.String(255))
+    dir = db.Column(db.String(255))
+
+
 '''
 ' Flask-Login is told to use the application’s custom anonymous user by setting
 ' its class in the login_manager.anonymous_user attribute.
diff --git a/web/app/results/__init__.py b/web/app/results/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f1f59a7c10787fc5c0f8d9b7a6107bfff9fe16d
--- /dev/null
+++ b/web/app/results/__init__.py
@@ -0,0 +1,5 @@
+from flask import Blueprint
+
+
+results = Blueprint('results', __name__)
+from . import views  # noqa
\ No newline at end of file
diff --git a/web/app/results/forms.py b/web/app/results/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/app/results/tables.py b/web/app/results/tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/web/app/results/views.py b/web/app/results/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a8af940fb7732e0e79121a6a3a14e92abd67f1f
--- /dev/null
+++ b/web/app/results/views.py
@@ -0,0 +1,46 @@
+from . import results
+from ..models import Result
+from flask import abort, render_template, current_app, request
+from flask_login import current_user, login_required
+from ..corpora.forms import DisplayOptionsForm
+import json
+import os
+
+
+@results.route('/<int:result_id>/details')
+@login_required
+def result_details(result_id):
+    '''
+    View to show metadate and details about on imported result file.
+    '''
+    result = Result.query.get_or_404(result_id)
+    if not (result.creator == current_user or current_user.is_administrator()):
+        abort(403)
+    return render_template('results/result_details.html.j2',
+                           result=result,
+                           title='Result Details')
+
+
+@results.route('/<int:result_id>/inspect')
+@login_required
+def result_inspect(result_id):
+    '''
+    View to inspect one importe result file in a corpus analysis like interface
+    '''
+    display_options_form = DisplayOptionsForm(
+        prefix='display-options-form',
+        result_context=request.args.get('context', 20),
+        results_per_page=request.args.get('results_per_page', 30))
+    result = Result.query.get_or_404(result_id)
+    result_file_path = os.path.join(current_app.config['NOPAQUE_STORAGE'],
+                                    result.file[0].dir,
+                                    result.file[0].filename)
+    with open(result_file_path, 'r') as result_json:
+        result_json = json.load(result_json)
+    if not (result.creator == current_user or current_user.is_administrator()):
+        abort(403)
+    return render_template('results/result_inspect.html.j2',
+                           display_options_form=display_options_form,
+                           result=result,
+                           result_json=result_json,
+                           title='Result Insepct')
diff --git a/web/app/services/forms.py b/web/app/services/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..13533ca6d3df76298ce46411bbe39f5e84de1509
--- /dev/null
+++ b/web/app/services/forms.py
@@ -0,0 +1,18 @@
+from flask_wtf import FlaskForm
+from werkzeug.utils import secure_filename
+from wtforms import FileField, SubmitField, ValidationError
+from wtforms.validators import DataRequired
+
+
+class ImportResultsForm(FlaskForm):
+    '''
+    Form used to import one result json file.
+    '''
+    file = FileField('File', validators=[DataRequired()])
+    submit = SubmitField()
+
+    def validate_file(self, field):
+        if not field.data.filename.lower().endswith('.json'):
+            raise ValidationError('File does not have an approved extension: '
+                                  '.json')
+        field.data.filename = secure_filename(field.data.filename)
diff --git a/web/app/services/tables.py b/web/app/services/tables.py
new file mode 100644
index 0000000000000000000000000000000000000000..e9d9389c3269fac10cf4c4513fb8b986ecb0fbbf
--- /dev/null
+++ b/web/app/services/tables.py
@@ -0,0 +1,65 @@
+from flask_table import Table, Col, DatetimeCol, LinkCol
+
+
+class ResultTable(Table):
+    '''
+    Declares the Table showing results. Declaration is column by column.
+    '''
+    classes = ['highlight', 'responsive-table']
+    query = Col('Query', column_html_attrs={'class': 'query'},
+                th_html_attrs={'class': 'sort',
+                               'data-sort': 'query'})
+    match_count = Col('Match count', column_html_attrs={'class':
+                                                        'match-count'},
+                      th_html_attrs={'class': 'sort',
+                                     'data-sort': 'match-count'})
+    corpus_name = Col('Corpus name', column_html_attrs={'class':
+                                                        'corpus-name'},
+                      th_html_attrs={'class': 'sort',
+                                     'data-sort': 'corpus-name'})
+    corpus_creation_date = DatetimeCol('Corpus creation date',
+                                       column_html_attrs={'class':
+                                                         'corpus-creation-  date'},  # noqa
+                                       th_html_attrs={'class': 'sort',
+                                                      'data-sort':
+                                                      'corpus-creation-date'},
+                                       datetime_format='dd/MM/yyyy, HH:mm:ss a')  # noqa
+    corpus_analysis_date = DatetimeCol('Date of result creation',
+                                       column_html_attrs={'class':
+                                                          'corpus-analysis-data'},  # noqa
+                                       th_html_attrs={'class': 'sort',
+                                                      'data-sort':
+                                                      'corpus-analysis-data'},
+                                       datetime_format='dd/MM/yyyy, HH:mm:ss a')  # noqa
+    corpus_type = Col('Result Type',
+                      column_html_attrs={'class':
+                                         'corpus-type'},
+                      th_html_attrs={'class': 'sort',
+                                     'data-sort':
+                                     'corpus-type'})
+    details = LinkCol('Details', 'results.result_details',
+                      url_kwargs=dict(result_id='id'),
+                      anchor_attrs={'class': 'waves-effect waves-light btn-floating'},  # noqa
+                      text_fallback='<i class="material-icons">info_outline</i>')  # noqa
+    inspect = LinkCol('Inspect', 'results.result_inspect',
+                      url_kwargs=dict(result_id='id'),
+                      anchor_attrs={'class': 'waves-effect waves-light btn-floating'},  # noqa
+                      text_fallback='<i class="material-icons">search</i>')  # noqa
+    # TODO: Maybe somehow fix taht there are two columns fpr two action buttons
+    # Or maybe just get rid of flask tables?
+
+
+class ResultItem(object):
+    '''
+    Describes one result item row.
+    '''
+
+    def __init__(self, query, match_count, corpus_name, corpus_creation_date,
+                 corpus_analysis_date, corpus_type, id):
+        self.query = query
+        self.match_count = match_count
+        self.corpus_name = corpus_name
+        self.corpus_creation_date = corpus_creation_date
+        self.corpus_analysis_date = corpus_analysis_date
+        self.corpus_type = corpus_type
+        self.id = id
diff --git a/web/app/services/views.py b/web/app/services/views.py
index 3c8d0b08097f409ec98974c7ad5868f5dbda0b46..2647f3a8fde837472dcb44b174f79734f7ea2ec4 100644
--- a/web/app/services/views.py
+++ b/web/app/services/views.py
@@ -1,13 +1,17 @@
 from flask import (abort, current_app, flash, make_response, render_template,
                    url_for)
 from flask_login import current_user, login_required
+from .forms import ImportResultsForm
 from werkzeug.utils import secure_filename
 from . import services
 from .. import db
 from ..jobs.forms import AddFileSetupJobForm, AddNLPJobForm, AddOCRJobForm
-from ..models import Job, JobInput
+from ..models import Job, JobInput, Result, ResultFile, User
+from .tables import ResultTable, ResultItem
 import json
 import os
+import html
+from datetime import datetime
 
 
 SERVICES = {'corpus_analysis': {'name': 'Corpus analysis'},
@@ -81,3 +85,84 @@ def service(service):
     return render_template('services/{}.html.j2'.format(service),
                            title=SERVICES[service]['name'],
                            add_job_form=add_job_form)
+
+
+@services.route('/import_results', methods=['GET', 'POST'])
+@login_required
+def import_results():
+    '''
+    View to import one json result file. Uses the ImportReultFileForm.
+    '''
+    # TODO: Build in a check if uploaded json is actually a result file and
+    # not something different
+    # Add the possibility to add several result files at once. 
+    import_results_form = ImportResultsForm(prefix='add-result-file-form')
+    if import_results_form.is_submitted():
+        if not import_results_form.validate():
+            return make_response(import_results_form.errors, 400)
+        # Save the file
+        # result creation only happens on file save to avoid creating a result
+        # object in the db everytime by just visiting the import_results page
+        result = Result(user_id=current_user.id)
+        db.session.add(result)
+        db.session.commit()
+        if not (result.creator == current_user
+                or current_user.is_administrator()):
+            abort(403)
+        dir = os.path.join(str(result.user_id),
+                           'results',
+                           'corpus_analysis_results',
+                           str(result.id))
+        abs_dir = os.path.join(current_app.config['NOPAQUE_STORAGE'], dir)
+        abs_file_path = os.path.join(abs_dir,
+                                     import_results_form.file.data.filename)
+        os.makedirs(abs_dir)
+        import_results_form.file.data.save(abs_file_path)
+        # Saves all needed metadata entries in one json field
+        with open(abs_file_path, 'r') as f:
+            corpus_metadata = json.load(f)
+        del corpus_metadata['matches']
+        del corpus_metadata['cpos_lookup']
+        result_file = ResultFile(
+            result_id=result.id,
+            dir=dir,
+            filename=import_results_form.file.data.filename)
+        result.corpus_metadata = corpus_metadata
+        db.session.add(result_file)
+        db.session.commit()
+        flash('Result file added!', 'result')
+        return make_response(
+            {'redirect_url': url_for('services.results')},
+            201)
+    return render_template('services/import_results.html.j2',
+                           import_results_form=import_results_form,
+                           title='Add corpus file')
+
+
+@services.route('/results')
+@login_required
+def results():
+    '''
+    Shows an overview of imported results.
+    '''
+    # get all results of current user
+    results = User.query.get(current_user.id).results
+    # create table row for every result#
+
+    def __p_time(time_str):
+        return datetime.strptime(time_str, '%Y-%m-%dT%H:%M:%S.%f')
+
+    items = [ResultItem(r.corpus_metadata['query'],
+                        r.corpus_metadata['match_count'],
+                        r.corpus_metadata['corpus_name'],
+                        __p_time(r.corpus_metadata['corpus_creation_date']),
+                        __p_time(r.corpus_metadata['corpus_analysis_date']),
+                        r.corpus_metadata['corpus_type'],
+                        r.id) for r in results]
+    # create table with items and save it as html
+    table = html.unescape(ResultTable(items).__html__())
+    # add class=list to table body with string replacement
+    table = table.replace('tbody', 'tbody class=list', 1)
+    return render_template('services/results.html.j2',
+                           title='Imported Results',
+                           table=table)
diff --git a/web/app/static/js/nopaque.InteractionElement.js b/web/app/static/js/nopaque.InteractionElement.js
index b3f8ec606c3a5cb8837c13306bf2e8eacef573d6..6e85b005a6166b80a381635d3bb10f69208960bf 100644
--- a/web/app/static/js/nopaque.InteractionElement.js
+++ b/web/app/static/js/nopaque.InteractionElement.js
@@ -29,4 +29,32 @@ class InteractionElement {
     let boundedCallback = callback["function"].bind(callback.bindThis);
     return boundedCallback;
   }
-}
\ No newline at end of file
+
+  static onChangeExecute(interactionElements) {
+    // checks if a change for every interactionElement happens and executes
+    // the callbacks accordingly
+    // TODO: This function scould be a static function of the Class InteractionElements
+    // This class does not exist yet. The Class InteractionElements should hold
+    // a list of InteractionElement objects. onChangeExecute loops over InteractionElements
+    // and executes the callbacks as mentioned accordingly. An additional
+    // InteractionElements Class is logically right but also makes things a little more
+    // complex. It is not yet decided.
+    for (let interaction of interactionElements) {
+      if (interaction.checkStatus) {
+        interaction.element.addEventListener("change", (event) => {
+          if (event.target.checked) {
+            let f_on = interaction.bindThisToCallback("on");
+            let args_on = interaction.callbacks.on.args;
+            f_on(...args_on);
+          } else if (!event.target.checked){
+            let f_off = interaction.bindThisToCallback("off");
+            let args_off = interaction.callbacks.off.args;
+            f_off(...args_off);
+          }
+        });
+      } else {
+        continue
+      }
+    };
+  }
+}
diff --git a/web/app/static/js/nopaque.callbacks.js b/web/app/static/js/nopaque.callbacks.js
index 45ee321ef139191123a026026b6bb513ef7ffe8d..1b3cba22cbfe3f6da53f8e057a95e14b67097836 100644
--- a/web/app/static/js/nopaque.callbacks.js
+++ b/web/app/static/js/nopaque.callbacks.js
@@ -63,7 +63,7 @@ function querySetup(payload) {
 
 // This callback is called on socket.on "query_results"
 // this handels the incoming result chunks
-function queryRenderResults(payload) {
+function queryRenderResults(payload, imported=false) {
   let resultItems;  // array of built html result items row element
   // This is called when results are transmitted and being recieved
   console.log("Current recieved chunk:", payload.chunk);
@@ -102,18 +102,23 @@ function queryRenderResults(payload) {
   console.log("Results recieved:", results.data);
   // upate progress status
   progress = payload.progress;  // global declaration
-  if (progress === 100) {
+  if (progress === 100 && !imported) {
     queryResultsProgressElement.classList.add("hide");
     queryResultsUserFeedbackElement.classList.add("hide");
     queryResultsExportElement.classList.remove("disabled");
     addToSubResultsElement.removeAttribute("disabled");
-    results.jsList.activateInspect();
     // inital expert mode check and sub results activation
+    results.jsList.activateInspect();
     if (addToSubResultsElement.checked) {
       results.jsList.activateAddToSubResults();
     }
     if (expertModeSwitchElement.checked) {
       results.jsList.expertModeOn("query-display");
     }
+  } else if (imported) {
+    results.jsList.activateInspect();
+    if (expertModeSwitchElement.checked) {
+      results.jsList.expertModeOn("query-display");
+    }
   }
 }
\ No newline at end of file
diff --git a/web/app/templates/corpora/analyse_corpus.html.j2 b/web/app/templates/corpora/analyse_corpus.html.j2
index cbf8ae77c8648a77a16de7fef9af4fad577504cb..d417d60bc329abfb99d06f7659ea562f757e7375 100644
--- a/web/app/templates/corpora/analyse_corpus.html.j2
+++ b/web/app/templates/corpora/analyse_corpus.html.j2
@@ -200,7 +200,6 @@
             </tbody>
           </table>
           <ul class="pagination paginationBottom"></ul>
-
         </div>
       </div>
     </div>
@@ -567,23 +566,7 @@
 
     // checks if a change for every interactionElement happens and executes
     // the callbacks accordingly
-    for (let interaction of interactionElements) {
-      if (interaction.checkStatus) {
-        interaction.element.addEventListener("change", (event) => {
-          if (event.target.checked) {
-            let f_on = interaction.bindThisToCallback("on");
-            let args_on = interaction.callbacks.on.args;
-            f_on(...args_on);
-          } else if (!event.target.checked){
-            let f_off = interaction.bindThisToCallback("off");
-            let args_off = interaction.callbacks.off.args;
-            f_off(...args_off);
-          }
-        });
-      } else {
-        continue
-      }
-    };
+    InteractionElement.onChangeExecute(interactionElements);
 
     // eventListener if pagination is used to apply new context size to new page
     // and also activate inspect match if progress is 100
diff --git a/web/app/templates/corpora/corpus.html.j2 b/web/app/templates/corpora/corpus.html.j2
index 3789af0cbf0bdf39ab5ac301977f7023f29e06e2..0a0a8c76ec6ef01d38354def5a94ff979bfac4d4 100644
--- a/web/app/templates/corpora/corpus.html.j2
+++ b/web/app/templates/corpora/corpus.html.j2
@@ -27,13 +27,13 @@
       <div class="row">
         <div class="col s12 m6">
           <div class="input-field">
-            <input disabled value="{{ corpus.creation_date.strftime('%m/%d/%Y, %H:%M:%S %p') }}" id="creation-date" type="text" class="validate">
+            <input disabled value="{{ corpus.creation_date.strftime('%d/%m/%Y, %H:%M:%S %p') }}" id="creation-date" type="text" class="validate">
             <label for="creation-date">Creation date</label>
           </div>
         </div>
         <div class="col s12 m6">
           <div class="input-field">
-            <input disabled value="{{ corpus.last_edited_date.strftime('%m/%d/%Y, %H:%M:%S %p') }}" id="last_edited_date" type="text" class="validate">
+            <input disabled value="{{ corpus.last_edited_date.strftime('%d/%m/%Y, %H:%M:%S %p') }}" id="last_edited_date" type="text" class="validate">
             <label for="creation-date">Last edited</label>
           </div>
         </div>
diff --git a/web/app/templates/jobs/job.html.j2 b/web/app/templates/jobs/job.html.j2
index e26448a05ff14b433d4eb967f6479ff8ca4ecd5c..3978183dee616ae06dfb9baf967ebe9d5bdefedc 100644
--- a/web/app/templates/jobs/job.html.j2
+++ b/web/app/templates/jobs/job.html.j2
@@ -36,7 +36,7 @@
 
         <div class="col s12 m6">
           <div class="input-field">
-            <input disabled id="creation-date" type="text" value="{{ job.creation_date.strftime('%m/%d/%Y, %H:%M:%S %p') }}">
+            <input disabled id="creation-date" type="text" value="{{ job.creation_date.strftime('%d/%m/%Y, %H:%M:%S %p') }}">
             <label for="creation-date">Creation date</label>
           </div>
         </div>
diff --git a/web/app/templates/main/dashboard.html.j2 b/web/app/templates/main/dashboard.html.j2
index 0bd1eafb4d07c35f226af49305a001a5e204188a..45569e6fb621c478485f3afa0700fe67e367ac4c 100644
--- a/web/app/templates/main/dashboard.html.j2
+++ b/web/app/templates/main/dashboard.html.j2
@@ -28,6 +28,7 @@
       <ul class="pagination"></ul>
     </div>
     <div class="card-action right-align">
+      <a class="waves-effect waves-light btn" href="{{ url_for('services.results') }}">Show Imported Results<i class="material-icons right">folder</i></a>
       <a class="waves-effect waves-light btn" href="{{ url_for('corpora.add_corpus') }}">New corpus<i class="material-icons right">add</i></a>
     </div>
   </div>
diff --git a/web/app/templates/results/result_details.html.j2 b/web/app/templates/results/result_details.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..73fe0f0a37b9bd083e3625817b8d5788df1ba631
--- /dev/null
+++ b/web/app/templates/results/result_details.html.j2
@@ -0,0 +1,118 @@
+{% extends "nopaque.html.j2" %}
+
+{% block page_content %}
+
+
+<div class="col s12">
+  <p>Below the metadata for the results from the Corpus
+    <i>{{ result.corpus_metadata.corpus_name }}</i> generated with the query
+    <i>{{ result.corpus_metadata.query }}</i> are shown.
+  </p>
+  <p>{{ texts_metadata }}</p>
+</div>
+
+<div class="col s12">
+  <div class="card">
+    <div class="card-content" id="results">
+      <table class="responsive-table highlight">
+        <thead>
+          <tr>
+            <th>Metadata Description</th>
+            <th>Value</th>
+          </tr>
+        </thead>
+        <tbody>
+          {% for pair in result.corpus_metadata|dictsort %}
+          <tr>
+            <td>{{ pair[0] }}</td>
+            {% if pair[0] == 'corpus_all_texts'
+               or pair[0] == 'text_lookup'  %}
+              <td>
+              <table>
+              {% for key, value  in pair[1].items()  %}
+                  <tr style="border-bottom: none;">
+                    <td>
+                      <i>{{ value['title'] }}</i> written
+                      by <i>{{ value['author'] }}</i>
+                      in <i>{{ value['publishing_year'] }}</i>
+                      <a class="waves-effect
+                                waves-light
+                                btn
+                                right
+                                more-text-detials"
+                         data-metadata-key="{{ pair[0] }}"
+                         data-text-key="{{ key }}"
+                         href="#modal-text-details">More
+                          <i class="material-icons right"
+                             data-metadata-key="{{ pair[0] }}"
+                             data-text-key="{{ key }}">
+                             info_outline
+                          </i>
+                      </a>
+                    </td>
+                  </tr>
+              {% endfor %}
+              </table>
+              </td>
+            {% else %}
+              <td>{{ pair[1] }}</td>
+            {% endif %}
+          </tr>
+          {% endfor %}
+        </tbody>
+      </table>
+    </div>
+    <div class="card-action right-align">
+      <a class="waves-effect waves-light btn" href="{{ url_for('services.import_results') }}">Inspect Results<i class="material-icons right">search</i></a>
+    </div>
+  </div>
+</div>
+
+<!-- Modal Structure -->
+<div id="modal-text-details" class="modal modal-fixed-footer">
+  <div class="modal-content">
+    <h4>Bibliographic data</h4>
+    <p id="bibliographic-data"></p>
+  </div>
+  <div class="modal-footer">
+    <a href="#!" class="modal-close waves-effect waves-green red btn">Close</a>
+  </div>
+</div>
+
+<script>
+var moreTextDetailsButtons;
+moreTextDetailsButtons = document.getElementsByClassName("more-text-detials");
+for (var btn of moreTextDetailsButtons) {
+  btn.onclick = () => {
+    let modal = document.getElementById("modal-text-details");
+    modal = M.Modal.init(modal, {"dismissible": true});
+    modal.open();
+    let metadataKey = event.target.dataset.metadataKey;
+    let textKey = event.target.dataset.textKey;
+    let textData = {{ result.corpus_metadata|tojson|safe }}[metadataKey][textKey];
+    console.log(textData);
+    let bibliographicData = document.getElementById("bibliographic-data");
+    bibliographicData.innerHTML = "";
+    let table = document.createElement("table");
+    for (let [key, value] of Object.entries(textData)) {
+      table.insertAdjacentHTML("afterbegin",
+      `
+        <tr>
+          <td>${key}</td>
+          <td>${value}</td>
+        </tr>
+      `);
+    }
+    table.insertAdjacentHTML("afterbegin",
+      `
+        <thead>
+          <th>Description</th>
+          <th>Value</th>
+        </thead>
+      `)
+    bibliographicData.appendChild(table);
+  }
+}
+</script>
+
+{% endblock %}
diff --git a/web/app/templates/results/result_inspect.html.j2 b/web/app/templates/results/result_inspect.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..73910f8251c005df132e128aa74659131878e249
--- /dev/null
+++ b/web/app/templates/results/result_inspect.html.j2
@@ -0,0 +1,211 @@
+{% extends "nopaque.html.j2" %}
+
+{% set headline = ' ' %}
+
+{% set full_width = True %}
+
+{% block page_content %}
+<div class="col s12" id="query-display">
+  <div class="card">
+    <div class="card-content" id="result-list" style="overflow: hidden;">
+      <div class="row" style="margin-bottom: 0px;">
+        <div class="col s12 m3 l3" id="results-info">
+          <div class="row section">
+            <h6 style="margin-top: 0px;">Infos</h6>
+            <div class="divider" style="margin-bottom: 10px;"></div>
+            <div class="col" id="infos">
+              <p>
+                Displaying
+                <span id="received-match-count">
+                </span> of
+                <span id="match-count"></span>
+                matches.
+                <br>
+                Matches occured in
+                <span id="text-lookup-count"></span>
+                corpus files:
+                <br>
+                <span id=text-titles></span>
+              </p>
+              <div class="progress hide" id="query-results-progress">
+                <div class="determinate" id="query-results-determinate"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class="col s12 m9 l9" id="actions-and-tools">
+          <div class="row section">
+            <div class="col s12 m3 l3" id="display">
+              <h6 style="margin-top: 0px;">Display</h6>
+              <div class="divider" style="margin-bottom: 10px;"></div>
+              <div class="row">
+                <div class="col s12">
+                  <form id="display-options-form">
+                    {{ M.render_field(display_options_form.results_per_page,
+                                      material_icon='format_list_numbered') }}
+                    {{ M.render_field(display_options_form.result_context,
+                                      material_icon='short_text') }}
+                    {{ M.render_field(display_options_form.expert_mode) }}
+                  </form>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- Table showing the query results -->
+        <div class="col s12">
+          <ul class="pagination paginationTop"></ul>
+          <table class="responsive-table highlight">
+            <thead>
+              <tr>
+                <th style="width: 2%">Nr.</th>
+                <th style="width: 3%">Title</th>
+                <th style="width: 25%">Left context</th>
+                <th style="width: 35%">Match</th>
+                <th style="width: 10%">Actions</th>
+                <th style="width: 25%">Right Context</th>
+              </tr>
+            </thead>
+            <tbody class="list" id="query-results">
+            </tbody>
+          </table>
+          <ul class="pagination paginationBottom"></ul>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<script src="{{ url_for('static', filename='js/nopaque.Results.js') }}">
+</script>
+<script src="{{ url_for('static', filename='js/nopaque.callbacks.js') }}">
+</script>
+<script src="{{ url_for('static', filename='js/nopaque.InteractionElement.js') }}">
+</script>
+<script>
+  // ###### global variables ######
+  var full_result_json;
+  var result_json;
+  var queryResultsDeterminateElement;  // The progress bar for recieved results
+  var receivedMatchCountElement;  // Nr. of loaded matches will be displayed in this element
+  var textLookupCountElement  // Nr of texts the matches occured in will be shown in this element
+  var textTitlesElement;  // matched text titles
+  var progress;  // global progress value
+  var queryResultsProgressElement;  // Div element holding the progress bar
+  var expertModeSwitchElement; // Expert mode switch Element
+  var matchCountElement;  // Total nr. of matches will be displayed in this element
+  var interactionElements;  // Interaction elements and their parameters
+
+  // ###### Defining local scope variables
+  let displayOptionsFormElement;  // Form holding the display informations
+  let resultItems;  // array of built html result items row element. This is called when results are transmitted and being recieved
+  let hitsPerPageInputElement;let contextPerItemElement;  // Form Element for display option
+  let paginationElements;
+
+  // ###### Initializing variables ######
+  displayOptionsFormElement = document.getElementById("display-options-form");
+  resultItems = [];
+  queryResultsDeterminateElement = document.getElementById("query-results-determinate");
+  receivedMatchCountElement = document.getElementById("received-match-count");
+  textLookupCountElement = document.getElementById("text-lookup-count");
+  textTitlesElement = document.getElementById("text-titles");
+  queryResultsProgressElement = document.getElementById("query-results-progress");
+  expertModeSwitchElement = document.getElementById("display-options-form-expert_mode");
+  matchCountElement = document.getElementById("match-count");
+  hitsPerPageInputElement = document.getElementById("display-options-form-results_per_page");
+  contextPerItemElement = document.getElementById("display-options-form-result_context");
+  paginationElements = document.getElementsByClassName("pagination");
+
+  // js list options
+  displayOptionsData = ResultsList.getDisplayOptions(displayOptionsFormElement);
+  resultsListOptions = {page: displayOptionsData["resultsPerPage"],
+  pagination: [{
+      name: "paginationTop",
+      paginationClass: "paginationTop",
+      innerWindow: 8,
+      outerWindow: 1
+    }, {
+      paginationClass: "paginationBottom",
+      innerWindow: 8,
+      outerWindow: 1
+    }],
+    valueNames: ["titles", "lc", "c", "rc", {data: ["index"]}],
+    item: `<span></span>`
+  };
+
+  document.addEventListener("DOMContentLoaded", () => {
+    // ###### recreating chunk structure to reuse callback queryRenderResults()
+    full_result_json = {{ result_json|tojson|safe }};
+    result_json = {};
+    result_json.chunk = {};
+    result_json.chunk["cpos_lookup"] = full_result_json.cpos_lookup;
+    result_json.chunk["cpos_ranges"] = full_result_json.cpos_ranges;
+    result_json.chunk["matches"] = full_result_json.matches;
+    result_json.chunk["text_lookup"] = full_result_json.text_lookup;
+
+    // Init corpus analysis components
+    data = new Data();
+    resultsList = new ResultsList("result-list", resultsListOptions);
+    resultsMetaData = new MetaData();
+    results = new Results(data, resultsList, resultsMetaData);
+    results.clearAll();  // inits some object keys and values
+    // TODO: save metadate into results.metaData
+
+    // setting some initial values for user feedback
+    matchCountElement.innerText = full_result_json.match_count;
+
+    // Initialization of interactionElemnts
+    // An interactionElement is an object identifing a switch or button via
+    // htmlID. Callbacks are set for these elements which will be triggered on
+    // a pagination interaction by the user or if the status of the element has
+    // been altered. (Like the switche has ben turned on or off).
+    interactionElements = new Array();
+    let expertModeInteraction = new InteractionElement("display-options-form-expert_mode");
+    expertModeInteraction.setCallback("on",
+                                      results.jsList.expertModeOn,
+                                      results.jsList,
+                                      ["query-display"])
+    expertModeInteraction.setCallback("off",
+                                      results.jsList.expertModeOff,
+                                      results.jsList,
+                                      ["query-display"])
+
+    let activateInspectInteraction = new InteractionElement("inspect",
+                                                            false);
+    activateInspectInteraction.setCallback("noCheck",
+                                            results.jsList.activateInspect,
+                                            results.jsList);
+
+    let changeContextInteraction = new InteractionElement("display-options-form-results_per_page",
+                                                          false);
+    changeContextInteraction.setCallback("noCheck",
+                                        results.jsList.changeContext,
+                                        results.jsList)
+    interactionElements.push(expertModeInteraction, activateInspectInteraction, changeContextInteraction);
+
+    // checks if a change for every interactionElement happens and executes
+    // the callbacks accordingly
+    InteractionElement.onChangeExecute(interactionElements);
+
+    // eventListener if pagination is used to apply new context size to new page
+    // and also activate inspect match if progress is 100
+    // also adds more interaction buttons like add to sub results
+    for (let element of paginationElements) {
+      element.addEventListener("click", (event) => {
+        results.jsList.pageChangeEventInteractionHandler(interactionElements);
+      });
+    }
+
+    // render results in table imported parameter is true
+    queryRenderResults(result_json, true)
+
+    // live update of hits per page if hits per page value is changed
+    let changeHitsPerPageBind = results.jsList.changeHitsPerPage.bind(results.jsList);
+    hitsPerPageInputElement.onchange = changeHitsPerPageBind;
+
+    // live update of lr context per item if context value is changed
+    contextPerItemElement.onchange = results.jsList.changeContext;
+  });
+</script>
+{% endblock %}
\ No newline at end of file
diff --git a/web/app/templates/services/corpus_analysis.html.j2 b/web/app/templates/services/corpus_analysis.html.j2
index 4512ef6ff321780973a052a96b08cb593c1a8f7a..2d6344eeb4657c15c5151ce2553df02e9de506b1 100644
--- a/web/app/templates/services/corpus_analysis.html.j2
+++ b/web/app/templates/services/corpus_analysis.html.j2
@@ -36,6 +36,7 @@
       <ul class="pagination"></ul>
     </div>
     <div class="card-action right-align">
+      <a class="waves-effect waves-light btn" href="{{ url_for('services.results') }}">Show Imported Results<i class="material-icons right">folder</i></a>
       <a class="waves-effect waves-light btn" href="{{ url_for('corpora.add_corpus') }}">New corpus<i class="material-icons right">add</i></a>
     </div>
   </div>
diff --git a/web/app/templates/services/import_results.html.j2 b/web/app/templates/services/import_results.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..f1e1d41fd9f41b5a5b2b4fc458ce6d3124a0e969
--- /dev/null
+++ b/web/app/templates/services/import_results.html.j2
@@ -0,0 +1,39 @@
+{% extends "nopaque.html.j2" %}
+
+{% block page_content %}
+<div class="col s12 m4">
+  <p>Fill out the following form to upload and view Results and Sub Results
+    exported from the Corpus analsis Tool.</p>
+  <a class="waves-effect waves-light btn" href="{{ url_for('main.dashboard') }}"><i class="material-icons left">arrow_back</i>Back to dashboard</a>
+</div>
+
+<div class="col s12 m8">
+  <form class="nopaque-submit-form" data-progress-modal="progress-modal">
+    <div class="card">
+      <div class="card-content">
+        {{ import_results_form.hidden_tag() }}
+        <div class="row">
+          <div class="col s12">
+            {{ M.render_field(import_results_form.file, accept='.json', placeholder='Choose your .json file') }}
+          </div>
+        </div>
+      </div>
+      <div class="card-action right-align">
+        {{ M.render_field(import_results_form.submit, material_icon='send') }}
+      </div>
+    </div>
+</form>
+</div>
+
+<div id="progress-modal" class="modal">
+  <div class="modal-content">
+    <h4><i class="material-icons prefix">file_upload</i> Uploading file...</h4>
+    <div class="progress">
+      <div class="determinate" style="width: 0%"></div>
+    </div>
+  </div>
+  <div class="modal-footer">
+    <a href="#!" class="modal-close waves-effect waves-light btn red abort-request">Cancel</a>
+  </div>
+</div>
+{% endblock %}
diff --git a/web/app/templates/services/results.html.j2 b/web/app/templates/services/results.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..2b01d6fdb86529fc176bb2904c80b0c61f77ba8a
--- /dev/null
+++ b/web/app/templates/services/results.html.j2
@@ -0,0 +1,52 @@
+{% extends "nopaque.html.j2" %}
+
+{% block page_content %}
+
+<div class="col s12">
+  <p>This is an overview of all your imported results.</p>
+</div>
+
+<div class="col s12">
+  <div class="card">
+    <div class="card-content" id="results">
+      <div class="input-field">
+        <i class="material-icons prefix">search</i>
+        <input id="search-results" class="search" type="search"></input>
+        <label for="search-results">Search results</label>
+      </div>
+      <ul class="pagination paginationTop"></ul>
+      {{ table }}
+      <ul class="pagination paginationBottom"></ul>
+      <ul class="pagination"></ul>
+    </div>
+    <div class="card-action right-align">
+      <a class="waves-effect waves-light btn" href="{{ url_for('services.import_results') }}">Import Results<i class="material-icons right">file_upload</i></a>
+    </div>
+  </div>
+</div>
+
+<script>
+var options = {page: 10,
+               pagination: [
+               {
+                 name: "paginationTop",
+                 paginationClass: "paginationTop",
+                 innerWindow: 8,
+                 outerWindow: 1
+               },
+               {
+                  paginationClass: "paginationBottom",
+                  innerWindow: 8,
+                  outerWindow: 1
+                }
+               ],
+               valueNames: ['query',
+                            'match-count',
+                            'corpus-name',
+                            'corpus-creation-date',
+                            'corpus-analysis-date',
+                            'corpus-type']
+              };
+var resultsList = new List('results', options);
+</script>
+{% endblock %}
diff --git a/web/migrations/versions/0d7aed934679_.py b/web/migrations/versions/0d7aed934679_.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c45d90ca8b4d94caa8f7c159043246882e58d55
--- /dev/null
+++ b/web/migrations/versions/0d7aed934679_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: 0d7aed934679
+Revises: b15366b25bea
+Create Date: 2020-06-30 13:57:48.782173
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '0d7aed934679'
+down_revision = 'b15366b25bea'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('result_files', sa.Column('corpus_metadata', sa.JSON(), nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('result_files', 'corpus_metadata')
+    # ### end Alembic commands ###
diff --git a/web/migrations/versions/318074622d14_.py b/web/migrations/versions/318074622d14_.py
new file mode 100644
index 0000000000000000000000000000000000000000..84dba226e3e6ecf43136b0e576a649b3c44db452
--- /dev/null
+++ b/web/migrations/versions/318074622d14_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: 318074622d14
+Revises: 0d7aed934679
+Create Date: 2020-06-30 14:00:18.968769
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = '318074622d14'
+down_revision = '0d7aed934679'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('result_files', 'corpus_metadata')
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('result_files', sa.Column('corpus_metadata', postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True))
+    # ### end Alembic commands ###
diff --git a/web/migrations/versions/389bcf564726_.py b/web/migrations/versions/389bcf564726_.py
new file mode 100644
index 0000000000000000000000000000000000000000..4244fcc11796c9132415c59c6b76b7c7c136c7f1
--- /dev/null
+++ b/web/migrations/versions/389bcf564726_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: 389bcf564726
+Revises: 318074622d14
+Create Date: 2020-06-30 14:03:33.384379
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '389bcf564726'
+down_revision = '318074622d14'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('result_files', sa.Column('corpus_metadata', sa.JSON(), nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('result_files', 'corpus_metadata')
+    # ### end Alembic commands ###
diff --git a/web/migrations/versions/b15366b25bea_.py b/web/migrations/versions/b15366b25bea_.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e90d9b00ffd5b17fcf825549f82472d45bca821
--- /dev/null
+++ b/web/migrations/versions/b15366b25bea_.py
@@ -0,0 +1,42 @@
+"""empty message
+
+Revision ID: b15366b25bea
+Revises: 4886241e0f5d
+Create Date: 2020-06-29 13:41:14.394680
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'b15366b25bea'
+down_revision = '4886241e0f5d'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table('results',
+    sa.Column('id', sa.Integer(), nullable=False),
+    sa.Column('user_id', sa.Integer(), nullable=True),
+    sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
+    sa.PrimaryKeyConstraint('id')
+    )
+    op.create_table('result_files',
+    sa.Column('id', sa.Integer(), nullable=False),
+    sa.Column('result_id', sa.Integer(), nullable=True),
+    sa.Column('filename', sa.String(length=255), nullable=True),
+    sa.Column('dir', sa.String(length=255), nullable=True),
+    sa.ForeignKeyConstraint(['result_id'], ['results.id'], ),
+    sa.PrimaryKeyConstraint('id')
+    )
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_table('result_files')
+    op.drop_table('results')
+    # ### end Alembic commands ###
diff --git a/web/migrations/versions/e256f5cac75d_.py b/web/migrations/versions/e256f5cac75d_.py
new file mode 100644
index 0000000000000000000000000000000000000000..3e810f2b5ffda9bf71fdf8874828eeb3f640ce99
--- /dev/null
+++ b/web/migrations/versions/e256f5cac75d_.py
@@ -0,0 +1,30 @@
+"""empty message
+
+Revision ID: e256f5cac75d
+Revises: 389bcf564726
+Create Date: 2020-07-01 07:45:24.637861
+
+"""
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
+
+# revision identifiers, used by Alembic.
+revision = 'e256f5cac75d'
+down_revision = '389bcf564726'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('result_files', 'corpus_metadata')
+    op.add_column('results', sa.Column('corpus_metadata', sa.JSON(), nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('results', 'corpus_metadata')
+    op.add_column('result_files', sa.Column('corpus_metadata', postgresql.JSON(astext_type=sa.Text()), autoincrement=False, nullable=True))
+    # ### end Alembic commands ###
diff --git a/web/nopaque.py b/web/nopaque.py
index ee20b8f744ae003d7b733abfe148c8d7476a4fa2..ed8ae04ba3338501438bd3bbb66cda0629422170 100644
--- a/web/nopaque.py
+++ b/web/nopaque.py
@@ -2,11 +2,11 @@ import eventlet
 eventlet.monkey_patch()  # noqa
 from app import create_app, db, socketio
 from app.models import (Corpus, CorpusFile, Job, JobInput, JobResult,
-                        NotificationData, NotificationEmailData, Role, User)
+                        NotificationData, NotificationEmailData, Result,
+                        ResultFile, Role, User)
 from flask_migrate import Migrate, upgrade
 import os
 
-
 app = create_app(os.getenv('FLASK_CONFIG') or 'default')
 migrate = Migrate(app, db)
 
@@ -21,6 +21,8 @@ def make_shell_context():
             'JobResult': JobResult,
             'NotificationData': NotificationData,
             'NotificationEmailData': NotificationEmailData,
+            'Result': Result,
+            'ResultFile': ResultFile,
             'Role': Role,
             'User': User}